Skip to main content
Tags and groups help you organize k6 test results for better analysis. They let you categorize metrics, filter results, and structure your tests logically.

What are tags?

Tags are key-value pairs attached to metrics, checks, and requests. They enable you to:
  • Filter metrics by request type, endpoint, or custom criteria
  • Set thresholds for specific tagged metrics
  • Analyze results by different dimensions
  • Compare performance across different parts of your test
k6 provides two types of tags:

System tags

Automatically added by k6Examples: status, method, url

User-defined tags

Custom tags you add to your testExamples: endpoint, type, priority

System tags

k6 automatically tags metrics with system-defined metadata:
TagDescriptionExample
statusHTTP status code200, 404, 500
methodHTTP methodGET, POST, PUT
urlRequest URLhttp://test.k6.io
nameRequest namehttp://test.k6.io
groupGroup path::login::submit
scenarioScenario namedefault
expected_responseExpected response flagtrue, false
protoProtocol versionHTTP/1.1, HTTP/2
errorError messageNetwork error string
tls_versionTLS versiontls1.2, tls1.3

Optional system tags

These tags are disabled by default but can be enabled:
TagDescription
vuVirtual user ID
iterIteration number
ipRemote server IP
ocsp_statusOCSP status
Enable them using the systemTags option:
export const options = {
  systemTags: ['status', 'method', 'url', 'vu', 'iter'],
};

User-defined tags

Add custom tags to requests, checks, and metrics:

Tag HTTP requests

import http from 'k6/http';

export default function () {
  http.get('https://quickpizza.grafana.com/', {
    tags: {
      type: 'homepage',
      priority: 'critical',
    },
  });
}

Tag checks

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

export default function () {
  const res = http.get('https://quickpizza.grafana.com/');
  
  check(
    res,
    { 'status is 200': (r) => r.status === 200 },
    { check_type: 'critical' }
  );
}

Tag custom metrics

import http from 'k6/http';
import { Trend } from 'k6/metrics';

const apiDuration = new Trend('api_duration');

export default function () {
  const res = http.get('https://quickpizza.grafana.com/api/pizzas');
  
  apiDuration.add(res.timings.duration, {
    endpoint: '/api/pizzas',
    version: 'v1',
  });
}

Test-wide tags

Set tags that apply to all metrics in your test:
export const options = {
  tags: {
    environment: 'production',
    team: 'backend',
    version: '1.2.3',
  },
};
Test-wide tags are useful for:
  • Comparing results across test runs
  • Filtering results in monitoring systems
  • Organizing tests by environment or team

Thresholds on tagged metrics

Set different thresholds for different tagged requests:
import http from 'k6/http';
import { sleep } from 'k6';

export const options = {
  thresholds: {
    'http_req_duration{type:API}': ['p(95)<500'],
    'http_req_duration{type:static}': ['p(95)<200'],
  },
};

export default function () {
  // API requests
  http.get('https://quickpizza.grafana.com/api/pizzas', {
    tags: { type: 'API' },
  });
  
  // Static content
  http.get('https://quickpizza.grafana.com/favicon.ico', {
    tags: { type: 'static' },
  });
  
  sleep(1);
}
This allows different performance requirements for different request types.

Groups

Groups organize test code into logical sections and automatically tag all operations within them.

Basic groups

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

export default function () {
  group('visit product listing', function () {
    http.get('https://example.com/products');
  });
  
  group('add to cart', function () {
    http.post('https://example.com/cart', {});
  });
  
  group('checkout', function () {
    http.post('https://example.com/checkout', {});
  });
  
  sleep(1);
}
All requests within a group are automatically tagged with group tag.

Nested groups

Groups can be nested for hierarchical organization:
import http from 'k6/http';
import { group } from 'k6';

export default function () {
  group('user workflow', function () {
    group('login', function () {
      http.get('https://example.com/login');
      http.post('https://example.com/auth', {});
    });
    
    group('browse', function () {
      http.get('https://example.com/products');
    });
  });
}
The group tag shows the full path: ::user workflow::login

Group duration metric

k6 automatically creates a group_duration metric for each group:
import http from 'k6/http';
import { group } from 'k6';

export const options = {
  thresholds: {
    'group_duration{group:::login}': ['avg<1000'],
    'group_duration{group:::checkout}': ['avg<2000'],
  },
};

export default function () {
  group('login', function () {
    http.get('https://example.com/login');
    http.post('https://example.com/auth', {});
  });
  
  group('checkout', function () {
    http.post('https://example.com/checkout', {});
  });
}

Dynamic tags from code

Set tags dynamically during test execution:
import http from 'k6/http';
import exec from 'k6/execution';
import { group } from 'k6';

export const options = {
  thresholds: {
    'http_reqs{container_group:main}': ['count==3'],
  },
};

export default function () {
  // Set dynamic tag
  exec.vu.tags.containerGroup = 'main';
  
  group('main', function () {
    http.get('https://test.k6.io');
    
    group('sub', function () {
      http.get('https://quickpizza.grafana.com/');
    });
    
    http.get('https://quickpizza.grafana.com/api/headers');
  });
  
  // Remove tag
  delete exec.vu.tags.containerGroup;
  
  http.get('https://quickpizza.grafana.com/api/delay/3');
}

Tagging stages

Use helper functions to tag operations by their test stage:
import http from 'k6/http';
import { tagWithCurrentStageIndex } from 'https://jslib.k6.io/k6-utils/1.3.0/index.js';

export const options = {
  stages: [
    { target: 5, duration: '2s' },
    { target: 10, duration: '5s' },
  ],
};

export default function () {
  tagWithCurrentStageIndex();
  
  // All requests tagged with current stage index
  http.get('https://test.k6.io'); // {stage: "0"} or {stage: "1"}
}

Complete example

Here’s a comprehensive example using tags and groups:
import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { Trend } from 'k6/metrics';

const apiDuration = new Trend('api_duration');

export const options = {
  vus: 10,
  duration: '30s',
  
  // Test-wide tags
  tags: {
    environment: 'production',
    test_type: 'load',
  },
  
  // Thresholds using tags
  thresholds: {
    'http_req_duration{type:api}': ['p(95)<500'],
    'http_req_duration{type:static}': ['p(95)<100'],
    'group_duration{group:::API Calls}': ['avg<400'],
    'checks{critical:true}': ['rate>0.99'],
  },
};

const baseUrl = 'https://quickpizza.grafana.com';

export default function () {
  group('Homepage', function () {
    const res = http.get(baseUrl, {
      tags: { type: 'static', page: 'home' },
    });
    
    check(
      res,
      { 'status is 200': (r) => r.status === 200 },
      { critical: 'true' }
    );
  });
  
  group('API Calls', function () {
    const res = http.get(`${baseUrl}/api/pizzas`, {
      tags: { type: 'api', endpoint: '/pizzas' },
    });
    
    apiDuration.add(res.timings.duration, {
      endpoint: '/pizzas',
    });
    
    check(
      res,
      { 'api status is 200': (r) => r.status === 200 },
      { critical: 'true' }
    );
  });
  
  sleep(1);
}

Best practices

Use descriptive tag names

Make tag names clear and consistent across your tests.

Tag by request type

Separate API, static content, and other request types with tags.

Group logical workflows

Use groups to organize user journeys and workflows.

Set thresholds per tag

Define different performance requirements for different request types.
Avoid one group per request. Wrapping every request in a group adds unnecessary complexity. Use groups for logical sections, not individual requests.

Build docs developers (and LLMs) love