Event Tags
Tags add context to your events, so a report can split an event's count by the things you care about, like the plan someone bought or where a signup came from.
What Are Tags?
Tags are labels attached to an event. In each report, an event's tags are listed under it with how many times each one appeared.
Important: Eventkiwi only tracks tag keys, not values. When you send a tag, only the key is stored and counted—the value is completely ignored.
For example:
?region=us→ stores tagregion?region=eu→ stores tagregion(same as above)?region→ stores tagregion(same as above)
All three create the same tag. Values don't matter.
Why Keys Only?
This design allows you to:
- Track the presence of characteristics (e.g., "has region tag")
- Keep aggregation simple and fast
- See which dimensions exist in your events
Need to Track Values?
If you need to distinguish between values like us vs eu, encode the value in the tag key itself:
# Instead of region=us, use:
?region-us
# Or:
?region:us
Cardinality is the number of unique values. Using value-encoded tags like region-us, region-eu, region-ap increases cardinality. High cardinality (hundreds of unique tags) can make reports harder to read and may impact performance. Use this approach sparingly and only when you need to distinguish between specific values.
Each Tag Is Counted on Its Own
Eventkiwi keeps one counter per tag, not one per combination of tags. If you
send checkout_completed with plan-pro and source-google, the report tells
you how many checkouts were plan-pro and how many were source-google, but
not how many were both.
This keeps reports short no matter how many tags you add. If one particular
combination matters to you, send it as its own tag, such as pro-from-google.
Adding Tags to Events
Tags can be added using either GET or POST requests. Any parameters (except reserved ones) are treated as tags.
Reserved Parameters
These query parameters are not treated as tags:
_id- Aggregator ID_apikey- API keyname- Event name
In a POST body, only name is reserved: every other key becomes a tag. Keep
_id and _apikey in the URL (or use the Authorization header), not in the body.
Tags with GET Requests
Add tags as URL query parameters. Values are completely ignored—only the parameter name (key) is stored.
# Single tag (value ignored)
curl "https://app.eventkiwi.com/api/event?_id=YOUR_AGGREGATOR_ID&_apikey=YOUR_API_KEY&name=user_signup&env=production"
# Result: stores tag "env"
# Multiple tags (all values ignored)
curl "https://app.eventkiwi.com/api/event?_id=YOUR_AGGREGATOR_ID&_apikey=YOUR_API_KEY&name=user_signup&env=prod®ion=us&platform=web"
# Result: stores tags "env", "region", "platform"
# Tag without value (same result)
curl "https://app.eventkiwi.com/api/event?_id=YOUR_AGGREGATOR_ID&_apikey=YOUR_API_KEY&name=checkout_completed&premium"
# Result: stores tag "premium"
# To track specific values, encode them in the key:
curl "https://app.eventkiwi.com/api/event?_id=YOUR_AGGREGATOR_ID&_apikey=YOUR_API_KEY&name=user_signup&env-production®ion-us"
# Result: stores tags "env-production", "region-us"
Tags with POST Requests
Add tags as additional fields in the JSON body (besides name). Values are completely ignored—only field names are stored as tags.
# Values are ignored
curl -X POST "https://app.eventkiwi.com/api/event?_id=YOUR_AGGREGATOR_ID" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "user_signup",
"env": "production",
"region": "us-east",
"platform": "mobile"
}'
# Result: stores tags "env", "region", "platform" (values ignored)
# To track specific values, encode them in field names:
curl -X POST "https://app.eventkiwi.com/api/event?_id=YOUR_AGGREGATOR_ID" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "user_signup",
"env-production": true,
"region-us-east": true,
"platform-mobile": true
}'
# Result: stores tags "env-production", "region-us-east", "platform-mobile"
Tag Normalization
Tags are automatically normalized to ensure consistency:
- Converted to lowercase -
PRODbecomesprod - Trimmed - Extra whitespace removed
- Special characters removed - Only URL-safe characters allowed: alphanumeric (a-z, 0-9), underscores (_), hyphens (-), and colons (:)
- Deduplicated - Duplicate tags are removed
Examples
| Original Tag | Normalized Tag |
|---|---|
ENV | env |
us-east-1 | us-east-1 |
premium_user | premium_user |
env:prod | env:prod |
Region@US | regionus |
mobile | mobile |
Tag Limitations
- Maximum tag key length: 100 characters
- Valid characters: Letters (a-z), numbers (0-9), hyphens (-), underscores (_), colons (:) (after normalization)
Tags that exceed the length limit or contain only invalid characters are silently ignored.
There is no enforced limit on the number of tags per event, but keep it reasonable for performance and report readability.
Common Tag Patterns
Remember: values are ignored, so encode values in tag keys when needed.
Environment Tags
Track events across different environments by encoding the environment in the tag name:
// Development
await sendEvent('api_error', { 'env-dev': true });
// Staging
await sendEvent('api_error', { 'env-staging': true });
// Production
await sendEvent('api_error', { 'env-production': true });
In reports: You'll see separate counts for env-dev, env-staging, and env-production.
Geographic Tags
Track events by location using value-encoded tags:
// US East region
await sendEvent('user_signup', {
'region-us-east': true,
'country-us': true
});
// EU West region
await sendEvent('user_signup', {
'region-eu-west': true,
'country-de': true
});
In reports: See signup counts per region and per country. Each tag is counted separately, so you won't see region and country paired up.
Platform Tags
Track events by platform or device:
// Web Chrome
await sendEvent('checkout_completed', {
'platform-web': true,
'browser-chrome': true
});
// Mobile iOS
await sendEvent('checkout_completed', {
'platform-mobile': true,
'os-ios': true
});
In reports: Compare checkout counts for platform-web vs platform-mobile.
User Type Tags
Segment events by user characteristics:
// Free user
await sendEvent('feature_used', { 'plan-free': true });
// Premium enterprise user
await sendEvent('feature_used', {
'plan-premium': true,
'tier-enterprise': true
});
// Trial user
await sendEvent('feature_used', { 'plan-trial': true });
In reports: Compare counts for plan-free, plan-premium, etc.
Status and Result Tags
Track success, failure, or status:
// Successful Stripe payment
await sendEvent('payment_processed', {
'status-success': true,
'method-stripe': true
});
// Failed payment (card declined)
await sendEvent('payment_processed', {
'status-failed': true,
'error-card-declined': true
});
In reports: See status-success vs status-failed counts.
Alternative: Single Presence Tags
If you only need to know that something happened (not the specific value), use simple tags:
// Just track that it has an environment (any environment)
await sendEvent('api_error', { env: true });
// Just track that user has a region
await sendEvent('user_signup', { region: true, country: true });
This keeps cardinality low but provides less detail.
Code Examples
JavaScript/TypeScript Helper Function
const AGGREGATOR_ID = process.env.AGGREGATOR_ID;
const API_KEY = process.env.API_KEY;
async function sendEvent(name, tags = {}) {
const response = await fetch(
`https://app.eventkiwi.com/api/event?_id=${AGGREGATOR_ID}`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name,
...tags
})
}
);
return response.json();
}
// Usage (values are ignored, so the value goes in the key)
await sendEvent('user_signup', {
'env-production': true,
'region-us-east': true,
'platform-web': true
});
Python Helper Function
import requests
import os
AGGREGATOR_ID = os.getenv('AGGREGATOR_ID')
API_KEY = os.getenv('API_KEY')
def send_event(name, tags=None):
if tags is None:
tags = {}
response = requests.post(
f'https://app.eventkiwi.com/api/event?_id={AGGREGATOR_ID}',
headers={
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
},
json={
'name': name,
**tags
}
)
return response.json()
# Usage (values are ignored, so the value goes in the key)
send_event('user_signup', {
'env-production': True,
'region-us-east': True,
'platform-web': True
})
PHP Helper Function
<?php
function sendEvent($name, $tags = []) {
$aggregatorId = getenv('AGGREGATOR_ID');
$apiKey = getenv('API_KEY');
$payload = array_merge(['name' => $name], $tags);
$ch = curl_init("https://app.eventkiwi.com/api/event?_id={$aggregatorId}");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// Usage (values are ignored, so the value goes in the key)
sendEvent('user_signup', [
'env-production' => true,
'region-us-east' => true,
'platform-web' => true
]);
Tag Strategy Best Practices
1. Plan Your Tag Schema
Define a consistent tagging strategy before implementation:
// Good: Consistent schema, sent as env-dev, region-us-east, plan-free, ...
const tagSchema = {
env: ['dev', 'staging', 'production'],
region: ['us-east', 'us-west', 'eu-west', 'ap-south'],
platform: ['web', 'mobile', 'api'],
plan: ['free', 'premium', 'enterprise']
};
2. Use Meaningful Tag Names
Choose tag names that clearly describe what they represent:
✅ Good:
env(environment)region(geographic region)platform(application platform)
❌ Poor:
e(too short)tag1(not descriptive)environmentVariable(too verbose)
3. Keep Tags Simple
One concept per tag:
✅ Good:
{ 'env-production': true, 'region-us': true }
❌ Poor:
{ 'env-production-region-us': true } // Mixing multiple concepts
Make a combined tag only when you need that exact combination in reports (see Each Tag Is Counted on Its Own).
4. Be Consistent
Use the same tag keys across different events:
// Consistent tagging across events
await sendEvent('user_signup', { 'env-production': true, 'platform-web': true });
await sendEvent('checkout_completed', { 'env-production': true, 'platform-web': true });
await sendEvent('api_error', { 'env-production': true, 'platform-api': true });
5. Don't Overuse Tags
Only add tags that provide value for analysis:
✅ Good: 3-5 meaningful tags per event
{ 'env-production': true, 'region-us': true, 'platform-web': true }
❌ Poor: Too many tags (high cardinality)
{
'env-production': true,
'region-us-east-1a': true, // Too specific
'platform-web': true,
'browser-chrome-120': true, // Version creates many unique tags
'os-windows-11-pro': true, // Too granular
'screen-1920x1080': true, // Creates hundreds of unique tags
// ... creates unmanageable number of unique tag keys
}
Remember: Each unique tag key appears separately in reports. With value-encoded tags like region-us, region-eu, region-ap, you create 3 unique tags. Be mindful of how many unique tags you create.
Viewing Tagged Events
- Email reports list every tag under its event, most frequent first, with how many times it appeared in the period.
- Telegram reports show the top 3 tags per event, to stay readable on a phone.
- The Event Log in the app shows each individual event with its tags.
A tag starts counting from the first event that carries it; earlier events are not backfilled.
Next Steps
- Authentication - Review authentication options
- Sending Events - Learn the basics of sending events