Creating a Calculated Metric
Combine one or more existing metrics into a new metric with a formula.
A calculated metric combines one or more existing metrics with a formula — no sourceId/fieldId/timeZone/refreshStatus at all. Sending a non-empty formula field is what makes a POST /metrics request a calculated metric; there is no separate endpoint or flag to set. The response reports "sourceType": "CALCULATED", and a calculated metric's id is always prefixed c_.
Formula syntax
The formula is a string built from arithmetic (+ - * / and parentheses) over one or more metric pointers:
<metricId>~(<queryOptions>)value;
<metricId>is the referenced metric'sid.<queryOptions>is an inline JSON object (or empty) controlling how that metric's value is fetched — e.g.{"aggregation":"sum"}. Use()for the metric's default aggregation.- The literal
value;suffix terminates the pointer. Everything outside a pointer (operators, numbers, parentheses grouping pointers) is plain arithmetic.
A formula referencing more than one metric pointer combines them with the usual operators — write multiple pointers side by side with an operator between them, same as any arithmetic expression.
{
"name": "Gross Margin",
"description": "Revenue minus hosting costs",
"formula": "9e21b4a1832443f0a911f5b8bb5cb2aa~()value; - c4f0d8a2b6e1479fa3c25d9e07b4f1c8~()value;",
"properties": {
"numberFormat": "NUMERIC"
}
}A percentage/ratio between two metrics, with an explicit aggregation on one of them (note the escaped quotes — the query options are JSON embedded inside the outer JSON string):
{
"name": "Hosting Cost % of Revenue",
"description": "Hosting costs as a share of revenue",
"formula": "(100 * c4f0d8a2b6e1479fa3c25d9e07b4f1c8~({\"aggregation\":\"sum\"})value;) / 9e21b4a1832443f0a911f5b8bb5cb2aa~()value;",
"properties": {
"numberFormat": "PERCENTAGE"
}
}Filtering a metric pointer
A pointer's <queryOptions> is the same object the POST /metrics/:metricId/query body takes, so it accepts a filter keyed by the referenced metric's dimension names. That is how a calculated metric narrows one of its inputs without needing a separate metric for the filtered slice.
The options are JSON embedded inside the outer JSON string, so every quote inside them is escaped. A member filter — revenue from Canada and Mexico only:
{
"name": "North American Revenue (excl. US)",
"formula": "9e21b4a1832443f0a911f5b8bb5cb2aa~({\"filter\":{\"Country\":{\"type\":\"member\",\"members\":[\"Canada\",\"Mexico\"],\"inverted\":false}}})value;",
"properties": {
"numberFormat": "CURRENCY",
"currencyCode": "USD"
}
}"inverted": true would instead take every country except those two.
A conditional filter matches members by their text, and sits alongside any other query option such as aggregation. Here the share of events whose name starts with f:
{
"name": "Share of \"f\" Events",
"formula": "(100 * 7d1c5e3b90a4482ea6f18c2b4d90e7a3~({\"aggregation\":\"sum\",\"filter\":{\"EventName\":{\"type\":\"multi\",\"filters\":[{\"type\":\"text_condition\",\"op\":\"starts_with\",\"inverted\":false,\"value\":\"f\"}]}}})value;) / 7d1c5e3b90a4482ea6f18c2b4d90e7a3~({\"aggregation\":\"sum\"})value;",
"properties": {
"numberFormat": "PERCENTAGE"
}
}The available op values are contains, starts_with and ends_with. Both filter shapes are described in full in Metrics.
Number format
properties.numberFormat is how the metric's value is rendered — send the one that suits the formula:
| Value | Renders as |
|---|---|
NUMERIC | a plain number |
CURRENCY | money, alongside properties.currencyCode |
PERCENTAGE | a percentage — a ratio formula is usually this |
DURATION | a length of time |
Omitting it, or properties entirely, is safe: POST /metrics defaults the format to NUMERIC.
Validation
A malformed formula, a circular dependency between calculated metrics, or referencing a metric id you don't have access to all return 400 (the last also independently returns 403 on the underlying share-rights check). There is also a company-configurable limit on nesting depth and on the number of distinct metrics one formula may reference; exceeding either is a 400.
Updated 6 days ago