When you’re load-testing an API, it’s not enough to hit the endpoint and measure speed. You also need to validate the data inside the JSON response to confirm the API behaves correctly under load.
k6 gives you a simple but powerful way to validate JSON using checks.
Table of contents
Open Table of contents
- Example API Response (Product List)
- Validate Status Codes
- Validate Response Time (k6 timings)
- Validate Content Type
- Validate Response Body and Size
- Validate Error Responses Under Load
- Validate Basic Structure (Shape)
- Validate Required Fields
- Validate Data Types
- Validate Value Ranges (Constraints)
- Validate Field Format (Patterns)
- Validate Nested JSON Objects
- Validate Arrays
- Validate Pagination Metadata (if applicable)
- Putting It All Together — Full k6 Script Snippet
- Summary — What You Can Validate in JSON With k6
Example API Response (Product List)
Imagine your API returns this:
[
{
"id": 101,
"name": "Wireless Mouse",
"price": 899,
"inStock": true,
"tags": ["electronics", "accessories"],
"rating": {
"average": 4.5,
"count": 132
}
}
]
We’ll validate this using k6.
Validate Status Codes
Validate if api has valid response code
check(res, {
"status is 200": r => r.status === 200,
"Status is not 500": r => r.status !== 500,
"Auth failed": r => r.status !== 401,
"status is 2XX": r => r.status >= 300 && r.status < 400,
});
It checks,
- API Response is 200
- API Response is not 500 (Server Errors) or 400 (Authencation Errors)
- API Response is within 300 to 399 (Useful for moved resources)
Validate Response Time (k6 timings)
k6 tracks detailed timing metrics inside every response.
You can validate timing directly:
check(res, {
"response under 500ms": r => r.timings.duration < 500,
"ttfb under 200ms": r => r.timings.waiting < 200
});
You can also inspect individual timing phases:
| Timing Metric | Meaning |
|---|---|
waiting | time waiting for the first byte (TTFB) |
duration | full request time |
sending | time to send request body |
receiving | time to download response |
Validate Content Type
Make sure you’re actually getting JSON:
check(res, {
"is JSON": r => r.headers["Content-Type"]?.includes("application/json"),
});
A backend returning HTML or text during load is more common than you think.
Validate Response Body and Size
Make sure api response body is not empty.
check(res, {
"response < 1MB": r => r.body.length > 0,
});
Make sure response is not more than specified size.
check(res, {
"response < 1MB": r => r.body.length < 1_000_000,
});
This prevents regressions like “suddenly returning 4 MB when it used to return 200 KB.”
Validate Error Responses Under Load
if (res.status !== 200) {
check(res, {
"error message exists": r => r.json("message") !== undefined,
});
}
You can choose size limits based on real-world expectations.
Validate Basic Structure (Shape)
Check that the response is a JSON array and not empty.
check(res, {
"is array": r => Array.isArray(r.json()),
"not empty": r => r.json().length > 0,
});
Validate Required Fields
Make sure each product has fields such as id, name, price that must exist.
const product = res.json()[0]; // Fetch first product from product list
check(product, {
"id exists": p => p.id !== undefined,
"name exists": p => p.name !== undefined,
"price exists": p => p.price !== undefined,
});
This prevents breaking changes in API contracts.
Validate Data Types
Confirm that each field has the type your application expects.
check(product, {
"id is number": p => typeof p.id === "number",
"name is string": p => typeof p.name === "string",
"price is number": p => typeof p.price === "number",
"inStock is boolean": p => typeof p.inStock === "boolean",
"tags is array": p => Array.isArray(p.tags),
});
If types shift under load (yes, this happens), your test catches it immediately.
Validate Value Ranges (Constraints)
Example rules you might enforce:
- price must be >= 0
- rating must be between 0–5
- rating count must never be negative
check(product, {
"price is positive": p => p.price >= 0,
"rating avg valid": p => p.rating.average >= 0 && p.rating.average <= 5,
"rating count positive": p => p.rating.count >= 0,
});
Validate Field Format (Patterns)
Example: product name should not be empty.
check(product, {
"name not empty": p => p.name.trim().length > 0,
});
Validate Nested JSON Objects
The rating key is a nested object. We can validate its structure and values too.
check(product.rating, {
"rating has avg": r => r.average !== undefined,
"rating has count": r => r.count !== undefined,
});
Nested checks prevent silent backend bugs.
Validate Arrays
The tags field is an array.
You can validate:
- it’s an array
- length is within expected boundaries
- values are strings
check(product, {
"tags array valid": p =>
Array.isArray(p.tags) &&
p.tags.every(t => typeof t === "string"),
});
Validate Pagination Metadata (if applicable)
Example endpoint:
/products?skip=0&limit=20
Typical metadata:
{
"total": 100,
"skip": 0,
"limit": 20,
"data": [ ... ]
}
Checks:
check(res.json(), {
"valid limit": j => j.limit > 0,
"valid skip": j => j.skip >= 0,
"valid data size": j => j.data.length <= j.limit,
});
Putting It All Together — Full k6 Script Snippet
import http from "k6/http";
import { check } from "k6";
export const options = {
vus: 1,
duration: "10s",
};
export default function () {
const res = http.get("https://example.com/api/products");
// Basic response checks
check(res, {
"status 200": r => r.status === 200,
"is JSON": r => r.headers["Content-Type"]?.includes("application/json"),
"duration < 500ms": r => r.timings.duration < 500,
"response < 1MB": r => r.body.length < 1_000_000,
});
const products = res.json();
if (!Array.isArray(products) || !products.length) return;
const p = products[0];
// Validate fields and types
check(p, {
"id number": p => typeof p.id === "number",
"name string": p => typeof p.name === "string",
"price number": p => typeof p.price === "number",
"inStock boolean": p => typeof p.inStock === "boolean",
"tags array": p => Array.isArray(p.tags),
});
// Value ranges
check(p, {
"price >= 0": p => p.price >= 0,
"rating valid": p =>
p.rating.average >= 0 &&
p.rating.average <= 5 &&
p.rating.count >= 0,
});
// Array checks
check(p.tags, {
"tags valid": t => t.every(x => typeof x === "string"),
});
}
k6-course/json-test.js
Summary — What You Can Validate in JSON With k6
Here’s the complete checklist you now know how to validate:
✓ Structure (object/array)
✓ Required fields
✓ Data types
✓ Value ranges (min/max)
✓ Formats (regex)
✓ Nested objects
✓ Arrays (length, types)
✓ Relationships between fields
✓ Pagination metadata
✓ Business rules
✓ Security-sensitive values
This demonstrates almost every validation technique in one place. This way we can ensures our load tests protect not just performance, but correctness and data integrity.