diff --git a/docs/stats-api.md b/docs/stats-api.md
index d4141089..227dcc38 100644
--- a/docs/stats-api.md
+++ b/docs/stats-api.md
@@ -5,7 +5,7 @@ toc_max_heading_level: 4
import { ApiV2Example, ExamplesTip } from '../src/js/apiv2-example.tsx';
import { Required, Optional } from '../src/js/api-helpers.tsx';
-import { getExampleCode } from '../src/js/examples.tsx';
+import { getExampleCode, EXAMPLE_RESPONSE_META } from '../src/js/examples.tsx';
import CodeBlock from '@theme/CodeBlock';
import { SiteContextProvider } from '../src/js/sites.tsx';
@@ -75,18 +75,49 @@ Metrics represent values to be calculated with the query.
Valid metrics are:
-| Metric name | Description |
-| --- | --- |
-| `visitors` | The number of unique visitors |
-| `visits` | The number of visits/sessions |
-| `pageviews` | The number of pageview events |
-| `views_per_visit` | The number of pageviews divided by the number of visits. Returns a floating point number. |
-| `bounce_rate` | Bounce rate percentage |
-| `visit_duration` | Visit duration in seconds |
-| `events` | The number of events (pageviews + custom events). When filtering by a goal, this metric corresponds to "Total Conversions" in the dashboard. |
-| `percentage` | The percentage of visitors of total who fall into this category: Requires: dimension list |
-| `conversion_rate` | The percentage of visitors who completed the goal. Requires: dimension list passed, an event:goal filter or event:goal dimension |
-| `group_conversion_rate` | The percentage of visitors who completed the goal with the same dimension. Requires: dimension list passed, an event:goal filter or event:goal dimension |
+| Metric name | Type | Description | Requirements |
+| --- | --- | --- | --- |
+| `visitors` | `int` | The number of unique visitors | |
+| `visits` | `int` | The number of visits/sessions | |
+| `pageviews` | `int` | The number of pageview events | |
+| `views_per_visit` | `float` | The number of pageviews divided by the number of visits. | |
+| `bounce_rate` | `float` | Bounce rate percentage | |
+| `visit_duration` | `int` | Visit duration in seconds | |
+| `events` | `int` | The number of events (pageviews + custom events). When filtering by a goal, this metric corresponds to "Total Conversions" in the dashboard. | |
+| `percentage` | `float` | The percentage of visitors of total who fall into this category: Requires: dimension list | Requires non-empty `dimensions` |
+| `conversion_rate` | `float` | The percentage of visitors who completed the goal. | Requires non-empty `dimensions`, `event:goal` filter or dimension being set |
+| `group_conversion_rate` | `float` | The percentage of visitors who completed the goal with the same dimension. Requires: dimension list passed, an event:goal filter or event:goal dimension | Requires non-empty `dimensions`, event:goal filter or dimension being set |
+| `average_revenue` | `Revenue` or null | Average revenue per revenue goal conversion | Requires [revenue goals](docs/ecommerce-revenue-tracking.md), `event:goal` filter or dimension for a relevant revenue goal. |
+| `total_revenue` | `Revenue` or null | Total revenue from revenue goal conversions | Requires [revenue goals](docs/ecommerce-revenue-tracking.md), `event:goal` filter or dimension for a relevant revenue goal. |
+
+
+
+ Read more about revenue metrics
+
+ To use revenue metrics, users should configure [revenue goals](docs/ecommerce-revenue-tracking.md).
+
+ Revenue metric response type has the following structure:
+
+ ```js
+ {
+ value: float,
+ currency: string, // e.g. "USD" or "EUR"
+ short: string, // e.g. "€500.2M"
+ long: string, // e.g. "€500,200,700.25"
+ }
+ ```
+
+ `long` and `short` options are human-friendly formatted results.
+
+ There are scenarios where revenue metrics can't be calculated. For example:
+ 1. When no revenue goals are configured
+ 2. No `event:goal` filter or dimension
+ 3. No revenue goal matches `event:goal` filter
+ 4. No `event:goal` dimension and filtered revenue goals have different currencies.
+
+ In these cases, revenue is returned as `null`s and `response.meta.metric_warning` value will have a warning for why the metric could not
+ be calculated. See [response.meta structure](#meta) and [example](#example-revenue-warning)
+
### dimensions {#dimensions}
@@ -313,10 +344,11 @@ Each result row contains:
- `dimensions` - values for each `dimension` listed in query. In the same order as query `dimensions`, empty if no dimensions requested.
- `metrics` - List of metric values, in the same order as query `metrics`
-
### meta
-Meta information about this query. Related: [include.imports](#include.imports) and [include.time_labels](#include.time_labels).
+Meta information about this query, including warnings and auxiliary data. Related: [include](#include).
+
+{EXAMPLE_RESPONSE_META}
### query
@@ -382,4 +414,15 @@ In this example, imported data could not be included due to dimension and filter
+
+### Revenue metrics {#example-revenue-metrics}
+
+
+
+### Revenue metrics could not be calculated {#example-revenue-warning}
+
+In this example, revenue metrics could not be calculated due to different currency filters. [More information](#metrics)
+
+
+
diff --git a/src/js/apiv2-examples/response-meta.json b/src/js/apiv2-examples/response-meta.json
new file mode 100644
index 00000000..e79c04a3
--- /dev/null
+++ b/src/js/apiv2-examples/response-meta.json
@@ -0,0 +1,32 @@
+{
+ // Whether imported data was included
+ // Only set if include.imports was set
+ "imports_included": false,
+
+ // Information about why including imported data failed
+ "imports_skip_reason": "unsupported_interval",
+ "imports_warning": "Imported stats are not included because the time dimension (i.e. the interval) is too short.",
+
+ // Warnings about specific metrics
+ // Currently only set if a revenue metric was used and was unable to be calculated
+ "metric_warnings": {
+ "total_revenue": {
+ "code": "no_revenue_goals_matching",
+ "warning": "Revenue metrics are null as there are no matching revenue goals."
+ }
+ },
+
+ // Only set if include.time_labels was set
+ "time_labels": [
+ "2024-09-10 00:00:00",
+ "2024-09-10 01:00:00",
+ "2024-09-10 02:00:00",
+ "2024-09-10 03:00:00",
+ "2024-09-10 04:00:00",
+ "2024-09-10 05:00:00",
+ "2024-09-10 06:00:00"
+ ],
+
+ // Only set if include.total_rows was set
+ "total_rows": 342
+}
diff --git a/src/js/apiv2-examples/revenue-metrics-query.json b/src/js/apiv2-examples/revenue-metrics-query.json
new file mode 100644
index 00000000..98ea02ea
--- /dev/null
+++ b/src/js/apiv2-examples/revenue-metrics-query.json
@@ -0,0 +1,6 @@
+{
+ "site_id": "dummy.site",
+ "metrics": ["total_revenue"],
+ "date_range": "all",
+ "dimensions": ["event:goal"]
+}
diff --git a/src/js/apiv2-examples/revenue-metrics-response.json b/src/js/apiv2-examples/revenue-metrics-response.json
new file mode 100644
index 00000000..94b09a56
--- /dev/null
+++ b/src/js/apiv2-examples/revenue-metrics-response.json
@@ -0,0 +1,30 @@
+{
+ "results": [
+ {
+ "dimensions": ["North America Purchases"],
+ "metrics": [
+ {
+ "short": "$96.3M",
+ "value": 96336315,
+ "long": "$96,336,315.00",
+ "currency": "USD"
+ }
+ ]
+ },
+ {
+ "dimensions": ["Visit /"],
+ "metrics": [null]
+ }
+ ],
+ "meta": {},
+ "query": {
+ "site_id": "dummy.site",
+ "metrics": ["total_revenue"],
+ "date_range": ["2021-12-14T00:00:00+00:00", "2024-12-11T23:59:59+00:00"],
+ "filters": [],
+ "dimensions": ["event:goal"],
+ "order_by": [["total_revenue", "desc"]],
+ "include": {},
+ "pagination": {"offset": 0, "limit": 10000}
+ }
+}
diff --git a/src/js/apiv2-examples/revenue-warning-query.json b/src/js/apiv2-examples/revenue-warning-query.json
new file mode 100644
index 00000000..37698939
--- /dev/null
+++ b/src/js/apiv2-examples/revenue-warning-query.json
@@ -0,0 +1,8 @@
+{
+ "site_id": "dummy.site",
+ "metrics": ["total_revenue"],
+ "date_range": "all",
+ "filters": [
+ ["is", "event:goal", ["PurchaseUSD", "PurchaseEUR"]]
+ ]
+}
diff --git a/src/js/apiv2-examples/revenue-warning-response.json b/src/js/apiv2-examples/revenue-warning-response.json
new file mode 100644
index 00000000..5e0cb5ea
--- /dev/null
+++ b/src/js/apiv2-examples/revenue-warning-response.json
@@ -0,0 +1,23 @@
+{
+ "results": [
+ {"metrics": [null], "dimensions": ["Visit /"]}
+ ],
+ "meta": {
+ "metric_warnings": {
+ "total_revenue": {
+ "code": "no_single_revenue_currency",
+ "warning": "Revenue metrics are null as there are multiple currencies for the selected event:goals."
+ }
+ }
+ },
+ "query": {
+ "site_id": "dummy.site",
+ "metrics": ["total_revenue"],
+ "date_range": ["2021-12-14T00:00:00+00:00", "2024-12-11T23:59:59+00:00"],
+ "filters": [["is", "event:goal", ["PurchaseUSD", "PurchaseEUR"]]],
+ "dimensions": [],
+ "order_by": [["total_revenue", "desc"]],
+ "include": {},
+ "pagination": {"offset": 0, "limit": 10000}
+ }
+}
diff --git a/src/js/examples.tsx b/src/js/examples.tsx
index 1781cc78..49e271e9 100644
--- a/src/js/examples.tsx
+++ b/src/js/examples.tsx
@@ -2,6 +2,8 @@ function read(path: string): string {
return require(`!!raw-loader?esModule=false!./${path}`)
}
+export const EXAMPLE_RESPONSE_META = read("apiv2-examples/response-meta.json")
+
const EXAMPLES = [
{
id: "example-aggregate",
@@ -74,6 +76,18 @@ const EXAMPLES = [
title: "Including imported data failed",
query: read("apiv2-examples/imports-bad-filter-query.json"),
exampleResponse: read("apiv2-examples/imports-bad-filter-response.json"),
+ },
+ {
+ id: "example-revenue-metrics",
+ title: "Revenue metrics",
+ query: read("apiv2-examples/revenue-metrics-query.json"),
+ exampleResponse: read("apiv2-examples/revenue-metrics-response.json"),
+ },
+ {
+ id: "example-revenue-warning",
+ title: "Revenue metrics could not be calculated",
+ query: read("apiv2-examples/revenue-warning-query.json"),
+ exampleResponse: read("apiv2-examples/revenue-warning-response.json"),
}
]