Most LoadBolt users are testing APIs. Here's how to do it well — from simple GETs to multi-step authenticated flows.
Your API is the backbone of your app. If it slows down under load, everything downstream — your frontend, your mobile app, your integrations — slows down too. Load testing your API before a launch, a big marketing push, or a Black Friday sale means you find the bottlenecks before your users do.
The simplest possible API test. Create a test in LoadBolt with your API URL and it auto-generates this for you:
import http from "k6/http";
import { check, sleep } from "k6";
export default function () {
const res = http.get(`${__ENV.BASE_URL}/api/health`);
check(res, { "status 200": (r) => r.status === 200 });
sleep(1);
}For endpoints that accept data:
import http from "k6/http";
import { check, sleep } from "k6";
export default function () {
const payload = JSON.stringify({
name: "Load Test Item",
category: "testing",
});
const res = http.post(`${__ENV.BASE_URL}/api/items`, payload, {
headers: { "Content-Type": "application/json" },
});
check(res, {
"created 201": (r) => r.status === 201,
});
sleep(1);
}Two common patterns for authenticated API requests:
import http from "k6/http";
// Bearer token (OAuth, JWT)
export default function () {
http.get(`${__ENV.BASE_URL}/api/me`, {
headers: { Authorization: `Bearer ${__ENV.API_TOKEN}` },
});
}
// API key header
// export default function () {
// http.get(`${__ENV.BASE_URL}/api/data`, {
// headers: { "X-API-Key": __ENV.API_KEY },
// });
// }Store tokens and keys in LoadBolt's variables UI — they get injected as environment variables at runtime.
A realistic test often walks through a full lifecycle: create a resource, read it, update it, delete it.
import http from "k6/http";
import { check, sleep } from "k6";
const headers = { "Content-Type": "application/json" };
export default function () {
const base = __ENV.BASE_URL;
// Create
const createRes = http.post(
`${base}/api/items`,
JSON.stringify({ name: `item-${Date.now()}` }),
{ headers }
);
check(createRes, { "created": (r) => r.status === 201 });
const id = createRes.json("id");
sleep(0.5);
// Read
const getRes = http.get(`${base}/api/items/${id}`);
check(getRes, { "fetched": (r) => r.status === 200 });
sleep(0.5);
// Update
const putRes = http.put(
`${base}/api/items/${id}`,
JSON.stringify({ name: "updated-item" }),
{ headers }
);
check(putRes, { "updated": (r) => r.status === 200 });
sleep(0.5);
// Delete
const delRes = http.del(`${base}/api/items/${id}`);
check(delRes, { "deleted": (r) => r.status === 200 || r.status === 204 });
sleep(1);
}GraphQL is just POST requests to a single endpoint with a query body:
import http from "k6/http";
import { check, sleep } from "k6";
export default function () {
const query = JSON.stringify({
query: `{
users(first: 10) {
id
name
email
}
}`,
});
const res = http.post(`${__ENV.BASE_URL}/graphql`, query, {
headers: { "Content-Type": "application/json" },
});
check(res, { "status 200": (r) => r.status === 200 });
sleep(1);
}Testing a webhook endpoint is just a POST with a representative payload:
import http from "k6/http";
import { check, sleep } from "k6";
export default function () {
const payload = JSON.stringify({
event: "order.completed",
data: { orderId: "ord_123", amount: 4999 },
});
const res = http.post(`${__ENV.BASE_URL}/api/webhooks/orders`, payload, {
headers: {
"Content-Type": "application/json",
"X-Webhook-Secret": __ENV.WEBHOOK_SECRET,
},
});
check(res, { "accepted": (r) => r.status === 200 });
sleep(0.5);
}Use LoadBolt's variables to keep your scripts reusable across environments. Set BASE_URL to https://staging.yourapp.com for staging tests, then swap to https://api.yourapp.com for production. Same script, different target.
Use check() to validate status codes:
import http from "k6/http";
import { check } from "k6";
export default function () {
const res = http.get(`${__ENV.BASE_URL}/api/items`);
check(res, {
"status is 200": (r) => r.status === 200,
"response time OK": (r) => r.timings.duration < 500,
});
}--discard-response-bodies enabled by default for better performance. This means res.body will be null — status code checks work fine, but you can't inspect the response body. If you need body-based checks, add discardResponseBodies: false to your script's options:export const options = {
discardResponseBodies: false,
};