> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/grafana/k6/llms.txt
> Use this file to discover all available pages before exploring further.

# Tags and Groups

> Organizing and filtering metrics with tags and groups

# 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

Tags are key-value pairs attached to metrics. They enable filtering and aggregation of test results.

### Why Use Tags?

* **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

## System Tags

k6 automatically adds system tags to all metrics:

| Tag           | Description      | Example                                   |
| ------------- | ---------------- | ----------------------------------------- |
| `proto`       | Protocol used    | `http/1.1`, `http/2`, `ws`                |
| `status`      | HTTP status code | `200`, `404`, `500`                       |
| `method`      | HTTP method      | `GET`, `POST`, `PUT`                      |
| `url`         | Full URL         | `https://quickpizza.grafana.com/api/menu` |
| `name`        | Request name/URL | `https://quickpizza.grafana.com`          |
| `group`       | Group name       | `::API Calls::Get Menu`                   |
| `check`       | Check name       | `status is 200`                           |
| `error`       | Error message    | (if request failed)                       |
| `error_code`  | Error code       | `1000`, `1101`                            |
| `tls_version` | TLS version      | `tls1.2`, `tls1.3`                        |
| `scenario`    | Scenario name    | `api_test`, `user_flow`                   |
| `service`     | Service name     | `api`, `web`                              |

### Controlling System Tags

Enable or disable system tags:

```javascript theme={null}
export let options = {
  systemTags: [
    'status',
    'method',
    'url',
    'name',
    'group',
    'check',
    'scenario',
  ],
};
```

<Note>
  Disabling system tags reduces memory usage and metrics cardinality, but limits filtering options.
</Note>

## Custom Tags

### Global Tags

Apply tags to all metrics in the test:

```javascript theme={null}
export let options = {
  tags: {
    environment: 'staging',
    team: 'backend',
    version: 'v1.2.3',
    region: 'us-east-1',
  },
};
```

### Request Tags

Add tags to specific HTTP requests:

```javascript theme={null}
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',
  });
}
```

### Scenario Tags

Tag all metrics from a scenario:

```javascript theme={null}
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',
      },
    },
  },
};
```

## Using Tags in Thresholds

Filter metrics by tags when defining thresholds:

```javascript theme={null}
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' });
}
```

<Warning>
  Tag values in threshold expressions are case-sensitive and must match exactly.
</Warning>

## Groups

Groups organize requests into logical sections, similar to folders. They help structure test logic and create hierarchical metrics.

### Basic Groups

```javascript theme={null}
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:

```javascript theme={null}
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: 'test@example.com',
        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:

```javascript theme={null}
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'],
  },
};
```

## Combining Tags and Groups

<CodeGroup>
  ```javascript HTTP Verbs Example theme={null}
  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",
      });
    });
  }
  ```

  ```javascript API Endpoints theme={null}
  import http from 'k6/http';
  import { group } from 'k6';

  export default function() {
    group('Public API', function() {
      http.get('https://quickpizza.grafana.com/api/menu', {
        tags: { api_type: 'public', auth: 'none' }
      });
    });
    
    group('Authenticated API', function() {
      http.get('https://quickpizza.grafana.com/api/orders', {
        tags: { api_type: 'private', auth: 'bearer' },
        headers: { 'Authorization': 'Bearer token123' },
      });
    });
  }
  ```
</CodeGroup>

## Tag and Group Best Practices

1. **Use consistent naming** - Establish naming conventions for tags and groups
2. **Avoid high cardinality** - Don't use unique values (like user IDs) as tags
3. **Tag strategically** - Add tags that help with analysis and debugging
4. **Group logically** - Organize by user flow, API endpoint type, or feature
5. **Limit nesting depth** - Keep group hierarchies shallow (2-3 levels max)
6. **Use scenario tags** - Tag entire scenarios for easy filtering

## Real-World Example

Here's a complete example combining tags and groups:

```javascript theme={null}
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: 'test@example.com',
    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:

```javascript theme={null}
// 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

* Review [Test Options](/writing-tests/test-options) for configuration
* Learn about [HTTP Requests](/writing-tests/http-requests) for making API calls
* Explore [Scenarios](/writing-tests/scenarios) for advanced test patterns
