Skip to content

Load Testing APIs with k6

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Load Testing APIs with k6. We cover key concepts, practical examples, and best practices to help you master this topic.

k6 is a modern load testing tool designed for developer workflows. It uses JavaScript for test scripting, supports virtual users with configurable ramping, provides built-in metrics, and integrates into CI/CD pipelines for performance regression testing.

What You'll Learn

  • k6 test script structure
  • Virtual users, stages, and ramping
  • Custom metrics and thresholds
  • HTTP request patterns for load tests
  • CI/CD integration

Why It Matters

Load testing validates that your API performs under expected and peak traffic. k6 catches performance regressions before they reach production. It is scriptable, maintainable, and CI-friendly.

Real-World Use

Grafana Labs uses k6 for Performance Testing. The k6 project itself is used by thousands of organizations. Many companies run k6 in CI to enforce performance budgets.

flowchart LR
    Script[k6 Script] --> Stages[Load Stages]
    Stages --> RampUp[Ramp Up: 0 -> 100 VUs]
    Stages --> Steady[Steady State: 100 VUs]
    Stages --> RampDown[Ramp Down: 100 -> 0 VUs]
    Script --> HTTP[HTTP Requests]
    HTTP --> Metrics[Metrics Collection]
    Metrics --> Thresholds[Thresholds Check]
    Thresholds --> Pass/Fail

Teacher Mindset

Start with a simple smoke test (1 VU). Add load stages gradually. Define thresholds for acceptable performance. Run tests in CI to catch regressions. Profile your API to find bottlenecks.

Code Examples

// Example 1: Basic k6 load test
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  vus: 10,
  duration: '30s',
  thresholds: {
    http_req_duration: ['p(95)<500'], // 95% of requests under 500ms
    http_req_failed: ['rate<0.01'],   // Less than 1% failure rate
  },
};

export default function () {
  const res = http.get('http://api.example.com/api/users');
  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 300ms': (r) => r.timings.duration < 300,
  });
  sleep(1);
}
// Example 2: Staged load test
import http from 'k6/http';

export const options = {
  stages: [
    { duration: '2m', target: 10 },   // Ramp up to 10 users
    { duration: '5m', target: 10 },   // Stay at 10 users
    { duration: '2m', target: 50 },   // Ramp up to 50 users
    { duration: '5m', target: 50 },   // Stay at 50 users
    { duration: '2m', target: 0 },    // Ramp down to 0
  ],
  thresholds: {
    http_req_duration: ['p(95)<1000'],
  },
};

export default function () {
  const payload = JSON.stringify({
    name: `User ${__VU}-${__ITER}`,
    email: `user${__VU}@test.com`,
  });

  const params = {
    headers: { 'Content-Type': 'application/json' },
  };

  http.post('http://api.example.com/api/users', payload, params);
  http.get('http://api.example.com/api/users');
}
// Example 3: Custom metrics and checks
import http from 'k6/http';
import { Trend, Rate } from 'k6/metrics';
import { check, sleep } from 'k6';

const myTrend = new Trend('api_duration');
const myRate = new Rate('success_rate');

export const options = {
  stages: [
    { duration: '1m', target: 20 },
    { duration: '3m', target: 20 },
    { duration: '1m', target: 0 },
  ],
};

export default function () {
  const res = http.get('http://api.example.com/api/products');

  myTrend.add(res.timings.duration);
  myRate.add(res.status === 200);

  check(res, {
    'products are paginated': (r) => r.json().hasOwnProperty('items'),
    'total count exists': (r) => r.json().hasOwnProperty('total'),
  });

  sleep(0.5);
}

Common Mistakes

  • Running load tests against production without coordination
  • Not using ramp-up stages (starting at full load causes connection storms)
  • Testing with unrealistic data or API patterns
  • Ignoring thresholds and only looking at average response times
  • Not running load tests regularly as part of CI

Practice

  1. Write a basic k6 script that hits a GET endpoint.
  2. Add stages for ramp-up, Steady State, and ramp-down.
  3. Set thresholds for response time and error rate.
  4. Add custom metrics for business-level success criteria.
  5. Challenge: Write a k6 test that simulates a user workflow (login, browse products, add to cart, checkout).

FAQ

What is the difference between k6 and JMeter?

k6 uses JavaScript and is developer-friendly. JMeter uses XML and has a GUI. k6 is designed for CI/CD integration.

Can k6 test GraphQL APIs?

Yes. Send GraphQL queries as POST requests with the query in the body.

How do I run k6 in CI?

Use the k6 Docker image or install k6 in your CI runner. Run k6 run script.js.

What metrics does k6 collect?

Request duration, request count, failure rate, bytes sent/received, VU count, and custom metrics.

Can k6 simulate real users?

Yes. Use stages for realistic traffic patterns. Add think time (sleep) between requests.

Mini Project

Write a k6 load test for an e-commerce API. Include stages: ramp to 10 users (2min), 50 users (5min), 100 users (5min), ramp down. Test three endpoints: list products, get product, create order. Set thresholds: p95 duration < 2s, error rate < 1%.

What's Next

Next, you will learn about Security Testing for APIs.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro