Troubleshoot MAU overages

  • Updated
  • Optimizely Web Experimentation
  • Optimizely Personalization
  • Optimizely Performance Edge
  • Optimizely Feature Experimentation

If your Monthly Usage Information reports more Monthly Active Users (MAUs) than expected, work through this article to identify the cause. The article covers three steps: enable bot filtering, run diagnostic queries, and use a consistent visitor ID across Optimizely products.

For background on MAUs and how each Optimizely product reports them, see Monitor monthly active users (MAUs).

Prerequisites

Before you start, gather the following access and exports so you can complete every step in this article:

The example queries in this article use Presto or Athena SQL syntax (CROSS JOIN UNNEST) to read events from S3. Adapt the syntax if you query your data with a different engine.

Diagnostic decision flow

Work through the following checks in order. Each one points you to the section that resolves it.

  1. Verify that bot filtering is enabled for every SDK and Web Experimentation project that sends events to Optimizely. If not, see Enable bot filtering.
  2. Run the Break down MAUs by client engine query from Run diagnostic queries. The result tells you where the MAUs come from.
    • Mostly from decisions – Review your decide API calls. You are likely sending inconsistent or non-unique userId values for the same visitor.
    • Mostly from conversions – Look for trackEvent calls that fire for visitors who never enter an experiment.
    • Both – Review both APIs.
  3. Confirm you use the same visitor ID across Web Experimentation and Feature Experimentation. If not, see Use a consistent visitor ID across products.
  4. Contact Optimizely Support if MAU overages continue after you work through the previous sections. Include the output of your diagnostic queries and a description of your visitor ID strategy.

Enable bot filtering

Bot filtering tells Optimizely to ignore traffic from user agents on a known bot list. Filtering this traffic prevents bots from inflating your MAU count.

  • Bot filtering is on by default for Optimizely Web Experimentation and for the Feature Experimentation JavaScript and React SDKs.
  • For all other Feature Experimentation SDKs, you must enable bot filtering manually. Set the $opt_user_agent attribute on the user context you pass to the SDK. Then, enable bot filtering in your Feature Experimentation project settings.

For step-by-step instructions, see the following articles:

Run diagnostic queries

The four queries in this section help you locate where your MAUs are coming from. Run them against your Experimentation Events Export. In every query, replace the example timestamp range with your billing cycle start and end timestamps.

Count MAUs in a billing cycle

This query returns the same MAU number you see on the Usage dashboard. Run it first to confirm that the data in your export matches what Optimizely is billing.

SELECT
    COUNT(DISTINCT visitor_id) AS MAU
FROM (
    -- Select visitor_id from Decisions table within the specified date range and account
    SELECT visitor_id
    FROM decisions
    WHERE
      timestamp BETWEEN TIMESTAMP '2026-04-01 00:00:00' AND TIMESTAMP '2026-04-30 00:00:00'

    UNION

    -- Select visitor_id from Conversions table within the specified date range and account
    SELECT visitor_id
    FROM events
    WHERE timestamp BETWEEN TIMESTAMP '2026-04-01 00:00:00' AND TIMESTAMP '2026-04-30 00:00:00'
) AS unique_visitors;

Break down MAUs by client engine

The client_engine column in the events export records which Optimizely product or SDK generated each event. This query groups MAUs by client_engine. The result shows which client engine drives the overage.

SELECT
    client_engine,
    COUNT(DISTINCT visitor_id) AS MAU
FROM (
    -- Select visitor_id and client_engine from Decisions table within the specified date range and account
    SELECT visitor_id, client_engine
    FROM decisions
    WHERE  timestamp BETWEEN TIMESTAMP '2026-04-01 00:00:00' AND TIMESTAMP '2026-04-30 00:00:00'

    UNION

    -- Select visitor_id and client_engine from Conversions table within the specified date range and account
    SELECT visitor_id, client_engine
    FROM events
    WHERE
   timestamp BETWEEN TIMESTAMP '2026-04-01 00:00:00' AND TIMESTAMP '2026-04-30 00:00:00'
) AS unique_visitors
GROUP BY client_engine;

When you know which client engine is generating the most MAUs, audit the decide and trackEvent calls in that engine. Common issues include the following:

  • Calling decide or decideAll for visitors who never reach an experiment.
  • Firing trackEvent calls outside the flows where experiments are activated.

The total returned by this query is higher than the total in the Usage dashboard. The dashboard counts a visitor exposed to multiple client engines as one MAU. This query counts the visitor once per engine.

Verify bot filtering is enabled

This query counts the events Optimizely received with the $opt_user_agent attribute set. The query excludes the JavaScript and React SDKs because they apply bot filtering automatically. If the result is 0, you have not enabled bot filtering for your other SDKs. Return to Enable bot filtering.

-- For Decisions table
SELECT
    'Decisions' AS table_name,
    COUNT(*) AS row_count
FROM decisions
CROSS JOIN UNNEST(attributes) AS t (attribute)
WHERE client_engine NOT IN ('javascript-sdk', 'js', 'react-sdk')
  AND attribute.name = '$opt_user_agent'

UNION ALL

-- For Conversions table
SELECT
    'Conversions' AS table_name,
    COUNT(*) AS row_count
FROM events
CROSS JOIN UNNEST(attributes) AS t (attribute)
WHERE client_engine NOT IN ('javascript-sdk', 'js', 'react-sdk')
  AND attribute.name = '$opt_user_agent';

Identify whether MAUs come from decisions or conversions

MAUs come from two event types: decision events (the decide API) and conversion events (the trackEvent API). A decision records when an SDK evaluates an experiment for a visitor. A conversion records when a visitor takes a tracked action. Splitting the MAU count by source narrows down where to investigate.

SELECT
    'Decisions' AS table_name,
    COUNT(DISTINCT visitor_id) AS MAU
FROM decisions
WHERE  timestamp BETWEEN TIMESTAMP '2026-04-01 00:00:00' AND TIMESTAMP '2026-04-30 00:00:00'

UNION ALL

SELECT
    'Conversions' AS table_name,
    COUNT(DISTINCT visitor_id) AS MAU
FROM events
WHERE timestamp BETWEEN TIMESTAMP '2026-04-01 00:00:00' AND TIMESTAMP '2026-04-30 00:00:00';
  • Audit your decide API calls if most MAUs come from decisions. You are likely sending inconsistent or non-unique userId values for the same visitor.
  • Look for leaked trackEvent calls if most MAUs come from conversions. A leaked call fires for a visitor who never enters an experiment.

Use a consistent visitor ID across products

Different visitor IDs in Web Experimentation and Feature Experimentation cause Optimizely to count the same person twice (once per product). The duplicate count inflates your MAU total.

By default, the products handle visitor IDs differently:

  • Web Experimentation generates its own visitor ID and stores it in the optimizelyEndUserId cookie.
  • Feature Experimentation uses the userId you pass in when you create the user context.

To prevent double-counting, configure both products to use the same identifier.

  • If you have an anonymous ID available on first page load, use it for both Web Experimentation and Feature Experimentation.
  • If not, reuse the identifier from your analytics tool (for example, the Google Analytics _ga cookie). Optimizely cannot read client-side cookies such as _ga on the first page load.

Web Experimentation supports overriding its default visitor ID. To configure the override, see Bring your own visitor ID (BYOID).

Contact Optimizely Support

If you complete the previous three steps and your MAU count is still higher than expected, contact Optimizely Support. Include the output of the diagnostic queries and a description of your visitor ID strategy.