← Back to Metrics

Average Order Value

Also known as: AOV, Average Basket Value, Average Transaction Value, Average Order Revenue, Revenue per Order

MonetizationmetricE-commerce: Cart & Checkout Funnel Analyticsaovbasket-sizeE-commerce

Average Order Value (AOV) is the net merchandise revenue, after discounts and excluding tax and shipping, earned per distinct completed order in the reporting period, matching GA4 Purchase revenue ÷ Transactions and Adobe Analytics Revenue ÷ Orders. It shows basket economics independently of traffic volume, so Merchandising and Finance can tell whether revenue changes come from larger baskets or simply from more orders.

Business Use Case

1️⃣ Merchandising: when AOV for new or returning customers on a channel drops more than 5% week over week, the merchandising manager reviews bundle placements and cross-sell recommendation slots on product and cart pages and reprioritises attach-rate products. 2️⃣ Marketing (Promotions): before a free-shipping threshold goes live, the promotions lead sets the threshold 15–25% above the current AOV. After launch, they compare AOV and order volume for exposed and holdout audiences to decide whether to keep, raise or withdraw the threshold. 3️⃣ Finance (FP&A): during the monthly forecast, the FP&A analyst breaks the revenue variance into order volume and AOV effects and revises the next-quarter revenue plan by channel and region. 4️⃣ Product (Checkout): when a cart or checkout release ships, the checkout product manager compares AOV and units per transaction between A/B variants. A variant that lifts conversion but lowers AOV only rolls out if revenue per session improves.

Business Criticality

High

Journey / Navigation Area

Monetization

Owning Function

Merchandising

Scope

Event

Formula

SUM(Net Merchandise Revenue of completed orders, after discounts, excl. tax and shipping) / COUNT(DISTINCT transaction_id)

Calculation Notes

1️⃣ Numerator basis: sum of order-level net merchandise revenue after line-item and order-level discounts. In GA4 this is the `purchase` `value`. In Adobe it is SUM(`productListItems[].priceTotal`) per deduplicated `commerce.order.purchaseID`, which is the Adobe Analytics Revenue basis (116.99 + 33 = 149.99 in the example). `commerce.order.priceTotal` is not used because in XDM it holds the tax-inclusive order total before shipping (161.99 with `taxAmount` 12). Tax, shipping and gift-wrap fees are excluded. Gift-card SKUs are left out of the GA4 value and out of productListItems at collection, so the figure is comparable with GA4 Purchase revenue and Adobe Revenue. 2️⃣ Denominator: COUNT(DISTINCT transaction_id) over `purchase` events only. Hits with a duplicate transaction_id (confirmation page reloads, back-button resubmits, server- and client-side double firing) count once, and their revenue is taken from the first occurrence only. 3️⃣ Exclude test orders (transaction IDs starting with `test`), internal-staff and debug traffic, and zero-value orders (e.g. 100% promo or warranty replacement orders) from both numerator and denominator. Otherwise the average is pulled down and orders are counted without matching revenue. Fraud-cancelled orders are removed only in the OMS-reconciled view, because the web-analytics queries cannot see fraud status. 4️⃣ Refunds: the reported (gross) AOV uses revenue at the time of purchase. Net AOV subtracts `refund` event value, or OMS return value, from the original order and keeps the order in the denominator unless it was cancelled before fulfilment. Always label which basis a report uses. 5️⃣ Currency: convert every order to the reporting currency at the transaction-date exchange rate before summing. Never average local-currency values across markets. 6️⃣ Timing: orders are attributed to the order-placed timestamp in the property time zone, not the fulfilment or settlement date. At session or user scope, AOV is still revenue ÷ orders over the aggregated set, never an average of per-session averages. 7️⃣ Outliers: B2B or bulk orders above the agreed threshold (commonly the 99.5th percentile of order value) are reported separately or winsorised in trend views. The threshold is documented so that period-over-period comparisons stay stable.

Technical Details

Measure TypeAverage
Aggregation WindowSession, User, Daily, Weekly, Monthly, Quarterly, Yearly

Events

PlatformEvent
Google Analytics 4purchase
Adobecommerce.purchases

Data Mappings

W3 Data Layer
{
  "transaction": {
    "transactionID": "ORD-20260916-10482",
    "profile": {
      "profileInfo": {
        "profileID": "CUST-558213"
      }
    },
    "total": {
      "basePrice": 162.99,
      "voucherCode": "RUN10",
      "voucherDiscount": 13,
      "currency": "USD",
      "taxRate": 0.08,
      "shipping": 5.99,
      "shippingMethod": "Standard Ground",
      "priceWithTax": 161.99,
      "transactionTotal": 167.98
    },
    "item": [
      {
        "productInfo": {
          "productID": "SKU-TRS-4471",
          "productName": "Trail Running Shoe",
          "sku": "SKU-TRS-4471-BLK-10"
        },
        "category": {
          "primaryCategory": "Footwear",
          "subCategory1": "Running Shoes"
        },
        "quantity": 1,
        "price": {
          "basePrice": 129.99,
          "voucherCode": "RUN10",
          "voucherDiscount": 13,
          "currency": "USD",
          "taxRate": 0.08,
          "priceWithTax": 126.35
        }
      },
      {
        "productInfo": {
          "productID": "SKU-SCK-0912",
          "productName": "Performance Running Socks 3-Pack",
          "sku": "SKU-SCK-0912-M"
        },
        "category": {
          "primaryCategory": "Accessories",
          "subCategory1": "Socks"
        },
        "quantity": 2,
        "price": {
          "basePrice": 16.5,
          "voucherDiscount": 0,
          "currency": "USD",
          "taxRate": 0.08,
          "priceWithTax": 17.82
        }
      }
    ]
  },
  "event": [
    {
      "eventInfo": {
        "eventName": "purchase",
        "eventAction": "transaction_complete",
        "type": "commerce"
      }
    }
  ]
}
GA4 Data Layer
{
  "event": "purchase",
  "ecommerce": {
    "transaction_id": "ORD-20260916-10482",
    "value": 149.99,
    "tax": 12,
    "shipping": 5.99,
    "currency": "USD",
    "coupon": "RUN10",
    "items": [
      {
        "item_id": "SKU-TRS-4471",
        "item_name": "Trail Running Shoe",
        "item_brand": "Northpeak",
        "item_category": "Footwear",
        "item_category2": "Running Shoes",
        "item_variant": "Black / 10",
        "coupon": "RUN10",
        "discount": 13,
        "price": 116.99,
        "quantity": 1,
        "index": 0
      },
      {
        "item_id": "SKU-SCK-0912",
        "item_name": "Performance Running Socks 3-Pack",
        "item_brand": "Northpeak",
        "item_category": "Accessories",
        "item_category2": "Socks",
        "item_variant": "M",
        "discount": 0,
        "price": 16.5,
        "quantity": 2,
        "index": 1
      }
    ]
  }
}
Adobe Client Data Layer
{
  "event": "commerce.purchases",
  "eventInfo": {
    "eventName": "commerce.purchases"
  },
  "commerce": {
    "purchases": {
      "value": 1
    },
    "order": {
      "purchaseID": "ORD-20260916-10482",
      "priceTotal": 161.99,
      "currencyCode": "USD",
      "taxAmount": 12,
      "discountAmount": 13,
      "promotionID": "RUN10"
    }
  },
  "shipping": {
    "shippingAmount": 5.99,
    "shippingMethod": "Standard Ground"
  },
  "productListItems": [
    {
      "SKU": "SKU-TRS-4471",
      "name": "Trail Running Shoe",
      "productCategories": [
        {
          "categoryID": "footwear-running-shoes",
          "categoryName": "Footwear"
        }
      ],
      "quantity": 1,
      "priceTotal": 116.99,
      "currencyCode": "USD"
    },
    {
      "SKU": "SKU-SCK-0912",
      "name": "Performance Running Socks 3-Pack",
      "productCategories": [
        {
          "categoryID": "accessories-socks",
          "categoryName": "Accessories"
        }
      ],
      "quantity": 2,
      "priceTotal": 33,
      "currencyCode": "USD"
    }
  ]
}
XDM Mapping
{
  "eventType": "commerce.purchases",
  "timestamp": "2026-09-16T14:32:07.000Z",
  "commerce": {
    "purchases": {
      "value": 1
    },
    "order": {
      "purchaseID": "ORD-20260916-10482",
      "priceTotal": 161.99,
      "currencyCode": "USD",
      "taxAmount": 12,
      "discountAmount": 13
    }
  },
  "shipping": {
    "shippingAmount": 5.99,
    "shippingMethod": "Standard Ground"
  },
  "productListItems": [
    {
      "SKU": "SKU-TRS-4471",
      "name": "Trail Running Shoe",
      "quantity": 1,
      "priceTotal": 116.99,
      "currencyCode": "USD"
    },
    {
      "SKU": "SKU-SCK-0912",
      "name": "Performance Running Socks 3-Pack",
      "quantity": 2,
      "priceTotal": 33,
      "currencyCode": "USD"
    }
  ],
  "_tenant": {
    "trafficType": "external"
  },
  "_experience": {
    "analytics": {
      "customDimensions": {
        "eVars": {
          "eVar5": "RUN10"
        }
      }
    }
  }
}

SQL Queries

Google Analytics 4 (BigQuery)
-- ==========================================
-- 1. GOOGLE BIGQUERY SQL
-- ==========================================
-- Metric: Average Order Value (gross AOV, order-date basis)
-- AOV = SUM(net merchandise revenue) / COUNT(DISTINCT transaction_id), a ratio of sums per day.
-- Revenue basis: ecommerce.purchase_revenue_in_usd carries the purchase `value` parameter, which the data layer
-- populates as merchandise revenue after discounts and excluding tax and shipping (tax and shipping travel
-- separately in ecommerce.tax_value / ecommerce.shipping_value and are not added). The _in_usd column applies the
-- transaction-date exchange rate, so markets are summed in one reporting currency.
-- No UNNEST(items) is used, so line items cannot multiply order revenue.
DECLARE start_date DATE DEFAULT DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY);
DECLARE end_date DATE DEFAULT DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY);

WITH purchase_events AS (
  SELECT
    PARSE_DATE('%Y%m%d', event_date) AS order_date,  -- event_date is already in the property time zone
    event_timestamp,
    ecommerce.transaction_id AS transaction_id,
    ecommerce.purchase_revenue_in_usd AS order_net_revenue,
    (SELECT MAX(IF(ep.key = 'traffic_type', ep.value.string_value, NULL))
       FROM UNNEST(event_params) AS ep) AS traffic_type,
    (SELECT MAX(IF(ep.key = 'debug_mode', COALESCE(CAST(ep.value.int_value AS STRING), ep.value.string_value), NULL))
       FROM UNNEST(event_params) AS ep) AS debug_mode
  FROM `your-project.analytics_123456789.events_*`
  WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', start_date) AND FORMAT_DATE('%Y%m%d', end_date)
    AND event_name = 'purchase'
    AND ecommerce.transaction_id IS NOT NULL
    AND ecommerce.transaction_id NOT IN ('', '(not set)')
),

deduplicated_orders AS (
  -- One row per transaction_id: reloads, resubmits and double firing keep only the first occurrence
  SELECT order_date, transaction_id, order_net_revenue
  FROM purchase_events
  WHERE COALESCE(traffic_type, '') != 'internal'
    AND COALESCE(debug_mode, '0') NOT IN ('1', 'true')
    AND NOT REGEXP_CONTAINS(LOWER(transaction_id), r'^test')
  QUALIFY ROW_NUMBER() OVER (PARTITION BY transaction_id ORDER BY event_timestamp) = 1
),

valid_orders AS (
  -- Zero-value orders (100% promo, warranty replacement) leave both numerator and denominator
  SELECT order_date, transaction_id, order_net_revenue
  FROM deduplicated_orders
  WHERE COALESCE(order_net_revenue, 0) > 0
)

SELECT
  order_date,
  COUNT(DISTINCT transaction_id) AS orders,
  ROUND(SUM(order_net_revenue), 2) AS net_revenue,
  ROUND(SAFE_DIVIDE(SUM(order_net_revenue), COUNT(DISTINCT transaction_id)), 2) AS average_order_value
FROM valid_orders
GROUP BY order_date
ORDER BY order_date;
Adobe Experience Platform (AEP Query Service)
-- ==========================================
-- 2. ADOBE AEP QUERY SERVICE SQL
-- ==========================================
-- Metric: Average Order Value (gross AOV, order-date basis)
-- AOV = SUM(productListItems[].priceTotal) / COUNT(DISTINCT commerce.order.purchaseID), a ratio of sums per day.
-- Revenue basis: in XDM commerce.order.priceTotal is the tax-inclusive order total after discounts (before
-- shipping), so it is not used. Net merchandise revenue is the sum of productListItems[].priceTotal, which matches
-- Adobe Analytics Revenue. Each purchaseID is deduplicated first, then its line items are exploded once and
-- re-aggregated by purchaseID, so line items cannot multiply order revenue.
-- Currency: restricted to one reporting currency. Convert orders to USD at the transaction-date rate before
-- loading, or change the currency code below.
WITH purchase_events AS (
  SELECT
    TO_DATE(FROM_UTC_TIMESTAMP(timestamp, 'America/New_York')) AS order_date,  -- set to the reporting time zone
    timestamp AS event_ts,
    commerce.order.purchaseID AS transaction_id,
    commerce.order.currencyCode AS currency_code,
    productListItems AS product_list_items
  FROM your_aep_web_experience_event_dataset
  WHERE timestamp >= TO_TIMESTAMP('2026-08-17 00:00:00')
    AND timestamp < TO_TIMESTAMP('2026-09-16 00:00:00')
    AND eventType = 'commerce.purchases'
    AND commerce.purchases.value = 1
    AND commerce.order.purchaseID IS NOT NULL
    AND TRIM(commerce.order.purchaseID) <> ''
    AND LOWER(commerce.order.purchaseID) NOT LIKE 'test%'
    AND commerce.order.currencyCode = 'USD'  -- single reporting currency
    -- _tenant.trafficType is the tenant custom field ('internal' = staff, 'debug' = QA/debug sessions),
    -- matching the GA4 traffic_type / debug_mode exclusions
    AND COALESCE(_tenant.trafficType, '') NOT IN ('internal', 'debug')
),

deduplicated_orders AS (
  -- One row per purchaseID: reloads, resubmits and double firing keep only the first occurrence
  SELECT order_date, transaction_id, currency_code, product_list_items
  FROM (
    SELECT
      order_date,
      transaction_id,
      currency_code,
      product_list_items,
      ROW_NUMBER() OVER (PARTITION BY transaction_id ORDER BY event_ts) AS rn
    FROM purchase_events
  ) ranked
  WHERE rn = 1
),

order_revenue AS (
  -- Explode line items once per deduplicated order and re-aggregate by purchaseID
  SELECT
    d.order_date,
    d.transaction_id,
    d.currency_code,
    SUM(CAST(pli.priceTotal AS DECIMAL(18, 2))) AS order_net_revenue
  FROM deduplicated_orders d
  LATERAL VIEW EXPLODE(d.product_list_items) items AS pli
  GROUP BY d.order_date, d.transaction_id, d.currency_code
),

valid_orders AS (
  -- Zero-value orders (100% promo, warranty replacement) leave both numerator and denominator
  SELECT order_date, transaction_id, currency_code, order_net_revenue
  FROM order_revenue
  WHERE COALESCE(order_net_revenue, 0) > 0
)

SELECT
  order_date,
  currency_code AS currency,
  COUNT(DISTINCT transaction_id) AS orders,
  ROUND(SUM(order_net_revenue), 2) AS net_revenue,
  ROUND(SUM(order_net_revenue) / NULLIF(COUNT(DISTINCT transaction_id), 0), 2) AS average_order_value
FROM valid_orders
GROUP BY order_date, currency_code
ORDER BY order_date;

Source Data

Digital Analytics (GA4 / Adobe Analytics) + Order Management (OMS)

Dependencies

Prerequisite: List the dependencies required for this Metric

Events:

purchase

Report Attributes

Date Default Channel Group Device Category New vs Returning Customer Coupon Code Country Currency

Recommended Reporting Views

Views
Executive Revenue DashboardMerchandising Performance DashboardPromotion Performance DashboardCheckout Optimization Dashboard

Segment Eligibility

true

Governance

Data SensitivityInternal

Derived KPIs

Governance

Created byswapnamagantius
Created on6/9/2026
Last modified bydevyendarm
Last modified on9/19/2026
Version1.0.0
Validation stateqa passed
StatusPUBLISHED

GitHub

View related Pull Requestcatalog/metrics/average-order-value.yml

Community Discussion