Tags and Groups
Tags and groups help you organize, filter, and analyze your k6 test metrics. They allow you to segment results by request type, scenario, user flow, or any custom dimension.
Tags are key-value pairs attached to metrics. They enable filtering and aggregation of test results.
Filter metrics - Create thresholds for specific requests or scenarios
Organize results - Group related requests together
Track metadata - Add context like environment, version, or team
Analyze patterns - Identify performance issues by tag dimensions
k6 automatically adds system tags to all metrics:
Tag Description Example protoProtocol used http/1.1, http/2, wsstatusHTTP status code 200, 404, 500methodHTTP method GET, POST, PUTurlFull URL https://quickpizza.grafana.com/api/menunameRequest name/URL https://quickpizza.grafana.comgroupGroup name ::API Calls::Get MenucheckCheck name status is 200errorError message (if request failed) error_codeError code 1000, 1101tls_versionTLS version tls1.2, tls1.3scenarioScenario name api_test, user_flowserviceService name api, web
Enable or disable system tags:
export let options = {
systemTags: [
'status' ,
'method' ,
'url' ,
'name' ,
'group' ,
'check' ,
'scenario' ,
],
};
Disabling system tags reduces memory usage and metrics cardinality, but limits filtering options.
Apply tags to all metrics in the test:
export let options = {
tags: {
environment: 'staging' ,
team: 'backend' ,
version: 'v1.2.3' ,
region: 'us-east-1' ,
},
};
Add tags to specific HTTP requests:
import http from 'k6/http' ;
import { Trend } from 'k6/metrics' ;
import { check } from 'k6' ;
let myTrend = new Trend ( 'my_trend' );
export default function () {
// Add tag to request metric data
let res = http . get ( 'http://httpbin.org/' , {
tags: {
my_tag: "I'm a tag" ,
endpoint: 'homepage' ,
api_version: 'v2' ,
}
});
// Add tag to check
check ( res , {
'status is 200' : ( r ) => r . status === 200
}, {
my_tag: "I'm a tag" ,
check_type: 'status' ,
});
// Add tag to custom metric
myTrend . add ( res . timings . connecting , {
my_tag: "I'm a tag" ,
phase: 'connection' ,
});
}
Tag all metrics from a scenario:
export let options = {
scenarios: {
api_test: {
executor: 'constant-vus' ,
vus: 10 ,
duration: '30s' ,
tags: {
test_type: 'api' ,
flow: 'user_registration' ,
},
},
browser_test: {
executor: 'constant-vus' ,
vus: 5 ,
duration: '30s' ,
tags: {
test_type: 'browser' ,
flow: 'checkout' ,
},
},
},
};
Filter metrics by tags when defining thresholds:
import http from 'k6/http' ;
export let options = {
thresholds: {
// All HTTP requests
'http_req_duration' : [ 'p(95)<500' ],
// Only POST requests
'http_req_duration{method:POST}' : [ 'p(95)<800' ],
// Specific URL
'http_req_duration{name:http://httpbin.org/post}' : [ 'max<1000' ],
// Multiple tag filters
'http_req_duration{method:GET,status:200}' : [ 'p(99)<600' ],
// Custom tags
'http_req_duration{endpoint:api}' : [ 'p(95)<400' ],
// Scenario-specific
'http_req_duration{scenario:api_test}' : [ 'p(95)<500' ],
},
};
export default function () {
http . get ( 'http://httpbin.org/' );
http . post ( 'http://httpbin.org/post' , { data: 'some data' });
}
Tag values in threshold expressions are case-sensitive and must match exactly.
Groups
Groups organize requests into logical sections, similar to folders. They help structure test logic and create hierarchical metrics.
Basic Groups
import http from 'k6/http' ;
import { group , check } from 'k6' ;
export default function () {
group ( 'API Calls' , function () {
let res = http . get ( 'https://quickpizza.grafana.com/api/menu' );
check ( res , { 'status is 200' : ( r ) => r . status === 200 });
});
group ( 'Static Assets' , function () {
http . get ( 'https://quickpizza.grafana.com/logo.png' );
http . get ( 'https://quickpizza.grafana.com/style.css' );
});
}
Nested Groups
Create hierarchical organization:
import http from 'k6/http' ;
import { group , check } from 'k6' ;
export default function () {
group ( 'User Flow' , function () {
group ( 'Login' , function () {
let loginRes = http . post ( 'https://quickpizza.grafana.com/api/login' , {
username: '[email protected] ' ,
password: 'password123' ,
});
check ( loginRes , { 'login successful' : ( r ) => r . status === 200 });
});
group ( 'Browse' , function () {
http . get ( 'https://quickpizza.grafana.com/api/menu' );
group ( 'View Item' , function () {
http . get ( 'https://quickpizza.grafana.com/api/items/42' );
});
});
group ( 'Checkout' , function () {
group ( 'Add to Cart' , function () {
http . post ( 'https://quickpizza.grafana.com/api/cart' , {
itemId: 42 ,
quantity: 1 ,
});
});
group ( 'Complete Order' , function () {
http . post ( 'https://quickpizza.grafana.com/api/orders' );
});
});
});
}
This creates a hierarchy like:
::User Flow
::User Flow::Login
::User Flow::Browse
::User Flow::Browse::View Item
::User Flow::Checkout
::User Flow::Checkout::Add to Cart
::User Flow::Checkout::Complete Order
Group Duration Metrics
k6 automatically tracks group duration:
export let options = {
thresholds: {
// Threshold for entire Login group
'group_duration{group:::User Flow::Login}' : [ 'p(95)<1000' ],
// Threshold for nested group
'group_duration{group:::User Flow::Checkout::Complete Order}' : [ 'p(99)<2000' ],
},
};
HTTP Verbs Example
API Endpoints
import http from "k6/http" ;
import { check , group } from "k6" ;
export default function () {
// GET request
group ( "GET" , function () {
let res = http . get ( "http://httpbin.org/get?verb=get" );
check ( res , {
"status is 200" : ( r ) => r . status === 200 ,
"is verb correct" : ( r ) => r . json (). args . verb === "get" ,
});
});
// POST request
group ( "POST" , function () {
let res = http . post ( "http://httpbin.org/post" , { verb: "post" });
check ( res , {
"status is 200" : ( r ) => r . status === 200 ,
"is verb correct" : ( r ) => r . json (). form . verb === "post" ,
});
});
// PUT request
group ( "PUT" , function () {
let res = http . put (
"http://httpbin.org/put" ,
JSON . stringify ({ verb: "put" }),
{ headers: { "Content-Type" : "application/json" }}
);
check ( res , {
"status is 200" : ( r ) => r . status === 200 ,
"is verb correct" : ( r ) => r . json (). json . verb === "put" ,
});
});
// DELETE request
group ( "DELETE" , function () {
let res = http . del ( "http://httpbin.org/delete?verb=delete" );
check ( res , {
"status is 200" : ( r ) => r . status === 200 ,
"is verb correct" : ( r ) => r . json (). args . verb === "delete" ,
});
});
}
Tag and Group Best Practices
Use consistent naming - Establish naming conventions for tags and groups
Avoid high cardinality - Don’t use unique values (like user IDs) as tags
Tag strategically - Add tags that help with analysis and debugging
Group logically - Organize by user flow, API endpoint type, or feature
Limit nesting depth - Keep group hierarchies shallow (2-3 levels max)
Use scenario tags - Tag entire scenarios for easy filtering
Real-World Example
Here’s a complete example combining tags and groups:
import http from 'k6/http' ;
import { group , check , sleep } from 'k6' ;
import { Rate } from 'k6/metrics' ;
let errorRate = new Rate ( 'errors' );
export let options = {
scenarios: {
api_flow: {
executor: 'constant-vus' ,
vus: 10 ,
duration: '1m' ,
tags: {
test_type: 'api' ,
flow: 'ecommerce' ,
},
},
},
tags: {
environment: 'staging' ,
version: 'v2.0.0' ,
},
thresholds: {
'http_req_duration{endpoint:api}' : [ 'p(95)<500' ],
'http_req_duration{group:::Browse::Search}' : [ 'p(99)<800' ],
'checks{flow:purchase}' : [ 'rate>0.95' ],
'errors' : [ 'rate<0.05' ],
},
};
export function setup () {
let loginRes = http . post ( 'https://quickpizza.grafana.com/api/login' , {
username: '[email protected] ' ,
password: 'password123' ,
}, {
tags: { endpoint: 'api' , operation: 'auth' }
});
return { token: loginRes . json ( 'token' ) };
}
export default function ( data ) {
let params = {
headers: { 'Authorization' : `Bearer ${ data . token } ` },
tags: { endpoint: 'api' },
};
group ( 'Browse' , function () {
group ( 'Search' , function () {
let searchRes = http . get (
'https://quickpizza.grafana.com/api/search?q=pizza' ,
{ ... params , tags: { ... params . tags , operation: 'search' } }
);
let searchOk = check ( searchRes , {
'search status 200' : ( r ) => r . status === 200 ,
'has results' : ( r ) => r . json ( 'results' ). length > 0 ,
}, { flow: 'browse' });
errorRate . add ( ! searchOk );
});
sleep ( 1 );
});
group ( 'Purchase' , function () {
let orderRes = http . post (
'https://quickpizza.grafana.com/api/orders' ,
JSON . stringify ({ items: [{ id: 42 , quantity: 1 }] }),
{
... params ,
tags: { ... params . tags , operation: 'create_order' },
headers: { ... params . headers , 'Content-Type' : 'application/json' },
}
);
let orderOk = check ( orderRes , {
'order created' : ( r ) => r . status === 201 ,
'has order id' : ( r ) => r . json ( 'orderId' ) !== undefined ,
}, { flow: 'purchase' });
errorRate . add ( ! orderOk );
sleep ( 1 );
});
}
Metrics Explorer Integration
Tags enable powerful filtering in metrics backends:
// Grafana query examples:
// - http_req_duration{scenario="api_test"}
// - http_req_duration{method="POST",status="200"}
// - group_duration{group=~".*Checkout.*"}
export let options = {
tags: {
team: 'platform' ,
service: 'api' ,
environment: 'production' ,
},
};
Next Steps