JWT API Load Test: Token Handling Template

Load test secured APIs using JWT bearer tokens. Handle token refresh, rotate test users, simulate 1000+ concurrent authenticated sessions.


Load test an API protected by JWT bearer tokens

Most API load tests fail at the door: 401 on every request because the token was never sent, or a login endpoint that gets hammered by the test itself. This template does it the way production traffic does: obtain a token once, send it in the Authorization: Bearer header on every measured request, and keep the token flow out of the numbers you are grading.

Configuration

SettingValueWhy
Virtual users200Enough concurrent bearer sessions to expose token validation cost, connection pools and rate limits.
Duration5 minutesShort enough to run on every deploy; long enough for p95 to settle after the ramp.
Ramp-up60 s in 4 steps50 users every 15 seconds; watch where latency starts climbing.
Requests2 to 4 authenticated endpointsA cheap read (GET /me), a list (GET /orders?limit=20), one write (POST). Leave the login endpoint out of the load, or give it its own small test.
HeaderAuthorization: Bearer <token>Set it once in the request headers (a header preset keeps it reusable across tests).
Think time0.5 to 1 sAPIs are called by code, not people; a short pause is enough to avoid an unrealistic burst.

Run this templateOpens the cloud test form with these values filled in. Free plan runs it at the free user limit; sign in or create a free account first.

The button prefills users, duration and ramp-up on the cloud test form. Add your endpoints and the Authorization header (issue a test token with a lifetime longer than the run), then start the test.

Getting the token in and keeping it out of the metrics

  • Long-lived test token. Issue one token for the test user with an expiry beyond the run (an hour is plenty) and paste it into the header. This is the simplest and it measures the API, not the identity provider.
  • Login once per virtual user. When tokens are short-lived, fetch the token in a setup step and reuse it; in the k6 version below that is the setup() function, which runs once and hands the token to every virtual user.
  • Expired or revoked tokens on purpose. A second, tiny run with a bad token tells you what a 401 costs under load. Rejecting a request should be cheaper than serving it; if p95 for 401s is close to p95 for 200s, token validation is hitting the database.

What to read in the results

  • 401 and 403 by endpoint. Any of these in the main run means the token did not reach a request or expired mid-run. Fix the setup, do not average it away.
  • p95 per endpoint. The read endpoints should sit well under the write; if GET /me is as slow as POST, token validation or a per-request permission lookup is the cost.
  • 429s. A rate limiter keyed on the token or the user will trip when 200 virtual users share one identity. That is a real finding about the limiter, not about capacity, and it is the reason to give each virtual user its own token in the second iteration of this test.

Pass/fail thresholds for this template

ThresholdTargetWhat a breach means
p95 response time< 300 msAuthenticated reads are slower than the same endpoints unauthenticated; find out by how much.
Error rate< 0.5%Any 401/403 in the measured requests is a test defect; 5xx under load is the finding.
Throughput> your target requests per secondSet it from the traffic you expect the API to serve at launch.

The same scenario as a k6 script

Upload this to a k6 cloud test when you need the token fetched at run time. Credentials go in environment variables, never in the script.

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '1m', target: 200 },
    { duration: '4m', target: 200 },
  ],
  thresholds: {
    http_req_duration: ['p(95)<300'],
    http_req_failed: ['rate<0.005'],
  },
};

const BASE = 'https://api.example.com';

// Runs once; the returned token is shared by every virtual user.
export function setup() {
  const res = http.post(`${BASE}/auth/login`, JSON.stringify({
    username: __ENV.LF_USER,
    password: __ENV.LF_PASS,
  }), { headers: { 'Content-Type': 'application/json' } });
  check(res, { 'login ok': (r) => r.status === 200 });
  return { token: res.json('access_token') };
}

export default function (data) {
  const params = { headers: { Authorization: `Bearer ${data.token}` } };
  const me = http.get(`${BASE}/me`, params);
  check(me, { 'me 200': (r) => r.status === 200 });
  const orders = http.get(`${BASE}/orders?limit=20`, params);
  check(orders, { 'orders 200': (r) => r.status === 200 });
  sleep(0.5 + Math.random() * 0.5);
}

FAQ on load testing JWT-protected APIs

Where do I put the token in a LoadFocus cloud test?

In the request headers of each request: name Authorization, value Bearer followed by the token. Save it as a header preset and it is available to every test on the team.

Should the login endpoint be part of the load test?

Not in the same run. Logging in 200 times a second tests your identity provider and skews every other number. Test the login endpoint on its own with a small, separate configuration.

The test returns 401 on every request. What is wrong?

Either the header is missing on some requests, the token expired during the run (issue one with a longer lifetime), or the API expects the token elsewhere (a cookie or an X-Api-Key header). Check the response body of a failed request in the Errors tab.

Does this work with OAuth 2.0 client credentials?

Yes. The flow is the same: request a token from the token endpoint once, then send it as a bearer header. The k6 version above swaps the login POST for the token endpoint call.

How fast is your website?

Elevate its speed and SEO seamlessly with our Free Speed Test.

Outgrown your testing tools?

Load test websites and APIs from 25+ cloud regions, monitor page speed and uptime, and get AI analysis that explains your results in plain English.Start for free
jmeter cloud load testing tool

Free Website Speed Test

Analyze your website's load speed and improve its performance with our free page speed checker.

×