Skip to main content

Statistics API

Burst Statistics v3.4.0 exposes a PHP API for querying analytics data programmatically. The entry point is the Statistics class, backed by the Query_Data value object that constructs and sanitizes SQL queries.


Architecture overview

Statistics (class-statistics.php)
└── Query_Data (class-query-data.php) ← value object, builds SQL
└── Goal_Statistics (class-goal-statistics.php) ← goal-specific queries

All queries target a set of custom database tables prefixed with {$wpdb->prefix}burst_. Execution time for every query is automatically stored in burst_query_stats for slow-query analysis.


Database tables

TablePurpose
burst_statisticsRaw page-hit records
burst_sessionsSession records (browser, device, referrer, etc.)
burst_browsersBrowser lookup table
burst_browser_versionsBrowser version lookup table
burst_platformsOS/platform lookup table
burst_devicesDevice type lookup table
burst_referrersReferrer lookup table
burst_goalsGoal definitions
burst_goal_statisticsGoal completion events
burst_known_uidsKnown visitor UIDs with first/last seen timestamps
burst_query_statsQuery execution time statistics

burst_statistics schema

:::caution Breaking change In v3.3.0 the columns browser_id, browser_version_id, platform_id, device_id, first_time_visit, and bounce were moved from burst_statistics to burst_sessions. Any custom SQL or filters that reference statistics.browser_id, statistics.device_id, statistics.first_time_visit, or statistics.bounce must be updated to use the sessions.* prefix. A database migration runs automatically on upgrade via the move_columns_to_sessions upgrade routine. :::

ColumnTypeDescription
IDint AUTO_INCREMENTPrimary key
page_urlvarchar(191)URL of the visited page
page_idint(11)WordPress post/page ID
page_typevarchar(191)Post type (e.g. post, page)
timeintUnix timestamp of the hit
uidvarchar(64)Visitor unique identifier
time_on_pageintTime spent on page (milliseconds)
parametersTEXTSerialized URL parameters
fragmentvarchar(255)URL fragment
session_idintFK → burst_sessions.ID

burst_sessions schema (selected columns)

The following columns were added to burst_sessions in v3.3.0:

ColumnTypeDescription
browser_idintFK → burst_browsers.ID
browser_version_idintFK → burst_browser_versions.ID
platform_idintFK → burst_platforms.ID
device_idintFK → burst_devices.ID
first_time_visittinyint1 if this session is the visitor's first
bouncetinyint1 if the session is a bounce (default 1)

burst_query_stats schema

ColumnTypeDescription
IDint AUTO_INCREMENTPrimary key
sql_hashvarchar(64)Deterministic hash of the query fingerprint payload
sql_queryTEXTRaw SQL that was executed
avg_execution_timefloatRolling average execution time in seconds
max_execution_timefloatSlowest recorded execution in seconds
min_execution_timefloatFastest recorded execution in seconds
execution_countintTotal number of executions recorded
last_updatedintUnix timestamp of the most recent update
date_range_daysintLength (in days) of the date range used by the query

Added in v3.4.0: the date_range_days column. The burst_query_stats table is pruned to the 100 slowest queries; queries that span more than 365 days are not recorded.


The Query_Data class

Query_Data is the query builder. Pass a stable query id and an associative array of options to its constructor; it validates and sanitizes every value before use. The query id is used to generate a deterministic fingerprint hash that groups related queries together in burst_query_stats, regardless of the exact timestamps passed in.

Show code
use Burst\Admin\Statistics\Query_Data;

$qd = new Query_Data(
'top_pages_by_device',
[
'date_start' => strtotime( '2024-01-01' ),
'date_end' => strtotime( '2024-01-31' ),
'select' => [ 'pageviews', 'visitors' ],
'filters' => [ 'device' => 'mobile' ],
'group_by' => 'page_url',
'order_by' => 'pageviews DESC',
'limit' => 10,
]
);

Constructor parameters

ParameterTypeDefaultDescription
$idstringRequired. Stable identifier for this query family; used for deterministic fingerprinting in burst_query_stats.
$argsarray[]Associative array of query options (see below).

Query options

OptionTypeDefaultDescription
date_startint0Start of the date range (Unix timestamp)
date_endint0End of the date range (Unix timestamp)
selectstring[]['*']Metrics to retrieve (see Available Metrics)
filtersarray[]Key/value filters (see Available Filters)
filter_exclusionsarray[]Per-filter mode: 'include' or 'exclude'; automatically set when a filter value is prefixed with !
group_bystring|string[][]Field(s) to group by
order_bystring|string[][]Field(s) to order by, e.g. 'pageviews DESC'
limitint0Maximum rows to return (0 = unlimited)
joinsarray[]Additional JOIN configurations
date_modifiersarray[]Date interval settings for period grouping
havingarray[]HAVING clause conditions
custom_selectstring''Raw SELECT fragment (admin-only; validated and prepared)
custom_select_parametersarray[]Prepared-statement parameters for custom_select
custom_wherestring''Raw WHERE fragment (admin-only; validated and prepared)
custom_where_parametersarray[]Prepared-statement parameters for custom_where
subquerystring''Wraps the main query as a subquery with the given alias
unionarray[]SQL strings to UNION with the main query
distinctboolfalseAdd DISTINCT to SELECT
windowarray[]Window function definitions keyed by alias
exclude_bouncesboolfalseAutomatically set to true when filters['bounces'] = 'exclude'

Strict mode

Query_Data automatically applies strict mode for non-administrator users. In strict mode:

  • Only a limited set of metrics may be selected (see burst_allowed_metric_keys filter).
  • Only a limited set of filter keys are accepted.
  • custom_select and custom_where are silently ignored.

The default strict-mode metric allowlist is:

pageviews, visitors, sessions, bounce_rate, avg_time_on_page,
first_time_visitors, page_url, referrer, device

Filter exclusions

Prefix any filter value with ! to exclude rather than include matching rows:

Show code
$qd = new Query_Data(
'pageviews_excluding_mobile',
[
'date_start' => $start,
'date_end' => $end,
'select' => [ 'pageviews', 'page_url' ],
'filters' => [ 'device' => '!mobile' ], // exclude mobile
]
);

Changed in v3.4.0: The referrer filter no longer includes NULL referrer rows when the filter is applied in include mode. NULL referrers are still included when the filter is applied in exclude mode (!example.com). Custom code that relied on NULL rows being returned alongside matching referrers in include mode must be updated.


Query fingerprinting

Every query executed via get_var(), get_row(), or get_results() is hashed and stored in burst_query_stats for slow-query analysis. The hash is deterministic: two queries with the same id, metrics, filters, group_by, order_by, and date range length (in days) will produce the same sql_hash, regardless of the actual timestamps.

Query_Data::get_id()

Returns the sanitized query id supplied to the constructor.

$id = $qd->get_id();
// 'top_pages_by_device'

Query_Data::get_fingerprint_payload()

Returns the canonical array used to build the fingerprint hash. Absolute timestamps are intentionally excluded; date_range_days is used instead so that the same query family hashes to the same value across days.

Show code
$payload = $qd->get_fingerprint_payload();
// [
// 'id' => 'top_pages_by_device',
// 'select' => [ 'pageviews', 'visitors' ],
// 'filters' => [ 'device' => 'mobile' ],
// 'filter_exclusions' => [],
// 'group_by' => [ 'page_url' ],
// 'order_by' => [ 'pageviews DESC' ],
// 'limit' => 10,
// 'distinct' => false,
// 'exclude_bounces' => false,
// 'date_range_days' => 30,
// ]

Query_Data::get_fingerprint_hash()

Returns the 64-bit FNV-1a hash of the JSON-encoded fingerprint payload.

$hash = $qd->get_fingerprint_hash();
// e.g. 'a1b2c3d4e5f60718'

Available metrics

These are the metric keys accepted in the select parameter:

KeyLabelNotes
pageviewsPageviews
visitorsVisitorsDistinct UIDs
sessionsSessionsDistinct session IDs
first_time_visitorsNew visitors
avg_time_on_pageAvg. time on pageMilliseconds
avg_session_durationAvg. session duration
bounce_rateBounce ratePercentage
bouncesBounced visitors
conversionsGoal completionsRequires goal_id filter
conversion_rateGoal conv. rate
page_urlPage
referrerReferrer
hostDomain
deviceDevice
browserBrowser
platformPlatform
device_idDevice (ID)Resolved from sessions.device_id
browser_idBrowser (ID)Resolved from sessions.browser_id
platform_idPlatform (ID)Resolved from sessions.platform_id
countCountAlias for pageviews
periodPeriodUsed with group_by: 'period'
timeTimeRaw timestamp column
time_on_pageTime on page
uidUID
page_idPage ID

Pro - CreatorAvailable in the Creator tier

The following metrics are available in Burst Pro:

KeyLabel
country_codeCountry
cityCity
stateState
continent_codeContinent
sourceSource (UTM)
mediumMedium (UTM)
campaignCampaign (UTM)
termTerm (UTM)
contentContent (UTM)
parameterURL Parameter
productProduct
salesSales
revenueRevenue
page_valuePage value
sales_conversion_rateSales conv. rate
avg_order_valueAvg. order value
adds_to_cartAdded to cart
entrancesEntrances
exit_rateExit rate
time_per_sessionTime per session

Available filters

Filters narrow query results. Pass them as an associative array to Query_Data via the filters key.

KeyDescriptionValues
page_urlFilter by page URLString, supports * wildcard suffix
page_idFilter by WordPress page IDInteger
page_typeFilter by post typePublic post type string
referrerFilter by referrer URLString
deviceFilter by device typedesktop, tablet, mobile, other
browserFilter by browser nameString
platformFilter by OS/platformString
device_idFilter by device lookup IDInteger
browser_idFilter by browser lookup IDInteger
platform_idFilter by platform lookup IDInteger
goal_idFilter by goal ID (admin only)Integer
bouncesInclude/exclude bounced sessions (admin only)include, exclude
new_visitorFilter first-time visitors (admin only)include, exclude
entry_exit_pagesFilter entry or exit pages (admin only)entry, exit
hostFilter by domain/host (admin only)String
parameterFilter by URL parameter (admin only)key, key=value

Pro - CreatorAvailable in the Creator tier

The following filters are available in Burst Pro:

KeyDescriptionValuesNotes
time_per_sessionFilter sessions by total time spent"MIN-MAX" or "MIN-" in secondsAdded in v3.3.0. E.g. "30-120" for 30–120 s; "600-" for 600 s and above.
Show code
$qd = new Query_Data(
'sessions_by_time_bucket',
[
'date_start' => $start,
'date_end' => $end,
'select' => [ 'pageviews', 'sessions' ],
'filters' => [ 'time_per_session' => '30-120' ], // sessions with 30–120 s total
]
);

The value is sanitized by sanitize_time_per_session_filter(). Both MIN and MAX are integers representing seconds; internally they are multiplied by 1000 to compare against the millisecond time_on_page column. If MAX is omitted (open-ended range), use the "MIN-" format.


The Statistics class

Obtain the singleton via:

$statistics = \Burst\burst_loader()->admin->statistics;

get_live_visitors_data()

Returns the number of visitors currently active on the site (within the last 10 minutes, excluding sessions that have been idle longer than the exit margin).

$count = $statistics->get_live_visitors_data();
// returns int

Added in v3.4.1: this method is also exposed to AI agents through the WordPress Abilities API as burst/live-visitors when the enable_abilities_api option is on. The ability response shape is [ 'visitors' => int ].


get_live_traffic_data()

Returns an array of the most recent page-hit objects within the last 10 minutes. Each object includes entry/exit/checkout flags.

$rows = $statistics->get_live_traffic_data();

Return shape (per object):

PropertyTypeDescription
active_timefloattime + time_on_page / 1000
utm_sourcestringSession referrer
page_urlstringPage URL
timeintHit timestamp
time_on_pageintTime on page (ms)
uidstringVisitor UID
page_idintWordPress page ID
entryboolWhether this is the visitor's entry hit
checkoutboolWhether this page is the checkout page
exitboolWhether the visitor appears to have exited

Added in v3.4.1: this method is also exposed to AI agents through the WordPress Abilities API as burst/live-traffic when the enable_abilities_api option is on. The ability accepts an optional limit (1–100, default 100) and returns [ 'items' => array, 'total' => int ].


get_today_data( array $args )

Returns summary statistics for today's dashboard block.

Parameters:

ParameterTypeDescription
$args['date_start']intStart of today (timestamp). Default 0.
$args['date_end']intEnd of today (timestamp). Default 0.

Return shape:

Show code
[
'live' => [ 'value' => string, 'tooltip' => string ],
'today' => [ 'value' => string, 'tooltip' => string ],
'mostViewed' => [ 'title' => string, 'value' => string, 'tooltip' => string ],
'referrer' => [ 'title' => string, 'value' => string, 'tooltip' => string ],
'pageviews' => [ 'title' => string, 'value' => string, 'tooltip' => string ],
'timeOnPage' => [ 'title' => string, 'value' => string, 'tooltip' => string ],
]

Added in v3.4.1: this method is also exposed to AI agents through the WordPress Abilities API as burst/today-summary when the enable_abilities_api option is on. The ability accepts optional date_start and date_end integers and returns a flattened, integer-only payload (live, today, most_viewed, top_referrer, pageviews, avg_time_on_page).


get_insights_data( array $args )

Returns chart-ready datasets and timestamp metadata for the insights panel.

Parameters:

ParameterTypeDescription
$args['date_start']intStart timestamp. Default 0.
$args['date_end']intEnd timestamp. Default 0.
$args['metrics']string[]Metrics to chart. Default ['pageviews', 'visitors'].
$args['filters']arrayFilters to apply. Default [].
$args['group_by']stringGrouping interval (auto, hour, day, week, month). Default 'auto'.

Changed in v3.4.0: the return shape replaces the pre-formatted labels array with raw timestamps plus interval and spans_multiple_years metadata, so the frontend can format dates locale-aware. A new group_by argument lets callers force a specific interval instead of deriving it from the range length.

Return shape:

Show code
[
'timestamps' => int[], // UTC period-start timestamps
'interval' => string, // 'hour' | 'day' | 'week' | 'month'
'spans_multiple_years' => bool, // true when the range crosses a calendar year
'datasets' => [
[
'data' => (int|float)[],
'backgroundColor' => string, // CSS custom property reference
'borderColor' => string, // CSS custom property reference
'label' => string,
'fill' => string,
],
// one entry per metric
],
]

The interval (hour / day / week / month) is determined automatically from the date range when group_by is 'auto':

RangeInterval
≤ 2 daysHour
3–48 daysDay
49–364 daysWeek
> 364 daysMonth

Added in v3.4.1: this method is also exposed to AI agents through the WordPress Abilities API as burst/data with type: 'insights' when the enable_abilities_api option is on. The ability reformats the response into a series array (one entry per requested metric, each with id, label, and [ timestamp, value ] points).

Changed in v3.4.2: the burst/data ability now accepts an interval hint (auto, hour, day, week, month) for insights requests. The value is forwarded to get_insights_data() as group_by. For backwards compatibility, when interval is omitted the first value of the legacy group_by input is used instead.


get_insights_date_modifiers( int $date_start, int $date_end, string $group_by = 'auto' )

Returns the date format strings and interval metadata used to build insight charts.

$modifiers = $statistics->get_insights_date_modifiers( $start, $end );

Changed in v3.4.0: added a third $group_by parameter ('auto', 'hour', 'day', 'week', 'month') to force a specific interval. The return shape replaces php_pretty_date_format with a spans_multiple_years boolean; date labels are now formatted on the frontend.

Return shape:

KeyTypeDescription
intervalstringhour, day, week, or month
interval_in_secondsintInterval length in seconds
nr_of_intervalsintNumber of data points in the range
sql_date_formatstringMySQL DATE_FORMAT() pattern
php_date_formatstringPHP date() pattern for array keys
spans_multiple_yearsbooltrue when date_start and date_end fall in different calendar years

get_compare_data( array $args )

Returns current-period and previous-period summary metrics for comparison widgets.

Parameters:

ParameterTypeDescription
$args['date_start']intStart of current period (timestamp).
$args['date_end']intEnd of current period (timestamp).
$args['compare_date_start']int|nullStart of comparison period. Defaults to the equivalent prior period.
$args['compare_date_end']int|nullEnd of comparison period.
$args['filters']arrayFilters to apply to both periods.

Return shape:

Show code
[
'current' => [
'pageviews' => int,
'sessions' => int,
'visitors' => int,
'first_time_visitors' => int,
'avg_time_on_page' => int,
'bounced_sessions' => int,
'bounce_rate' => float,
],
'previous' => [
'pageviews' => int,
'sessions' => int,
'visitors' => int,
'bounced_sessions' => int,
'bounce_rate' => float,
],
]

get_compare_goals_data( array $args )

Pro - CreatorAvailable in the Creator tier

Returns current-period and previous-period goal/conversion metrics.

Goal Conversion Tracking

Parameters:

ParameterTypeDescription
$args['date_start']intStart timestamp.
$args['date_end']intEnd timestamp.
$args['filters']arrayFilters, may include goal_id.
$args['compare_date_start']int|nullStart of comparison period.
$args['compare_date_end']int|nullEnd of comparison period.

Return shape:

Show code
[
'view' => 'goals',
'current' => [
'pageviews' => int,
'visitors' => int,
'sessions' => int,
'first_time_visitors' => int,
'conversions' => int,
'conversion_rate' => float,
],
'previous' => [
'pageviews' => int,
'visitors' => int,
'sessions' => int,
'conversions' => int,
'conversion_rate' => float,
],
]

get_data( array $select, int $start, int $end, array $filters )

Low-level single-row query. Returns one associative array of metric values for the given period.

Show code
$row = $statistics->get_data(
[ 'pageviews', 'visitors', 'sessions' ],
strtotime( '2024-01-01' ),
strtotime( '2024-01-31' ),
[ 'device' => 'mobile' ]
);
// [ 'pageviews' => 1200, 'visitors' => 340, 'sessions' => 400 ]

get_devices_title_and_value_data( array $args )

Returns pageview counts grouped by device type.

Parameters:

ParameterTypeDescription
$args['date_start']intStart timestamp. Default 0.
$args['date_end']intEnd timestamp. Default 0.
$args['filters']arrayFilters to apply. Default [].

Return shape:

Show code
[
'all' => [ 'count' => int ],
'desktop' => [ 'count' => int ],
'tablet' => [ 'count' => int ],
'mobile' => [ 'count' => int ],
'other' => [ 'count' => int ],
]

get_devices_subtitle_data( array $args )

Returns the most common browser and OS for each device type.

Parameters:

ParameterTypeDescription
$args['date_start']intStart timestamp. Default 0.
$args['date_end']intEnd timestamp. Default 0.
$args['filters']arrayFilters to apply. Default [].

Return shape:

Show code
[
'desktop' => [ 'os' => string, 'browser' => string, 'device_id' => int ],
'tablet' => [ 'os' => string, 'browser' => string, 'device_id' => int ],
'mobile' => [ 'os' => string, 'browser' => string, 'device_id' => int ],
'other' => [ 'os' => string, 'browser' => string, 'device_id' => int ],
]

get_datatables_data( array $args )

Returns data for the sortable data table components in the dashboard. Colors emitted by this method are CSS custom-property strings (e.g. var(--color-blue-400)); the frontend's METRIC_COLORS map takes precedence when rendering.

Parameters:

ParameterTypeDescription
$args['date_start']intStart timestamp. Default 0.
$args['date_end']intEnd timestamp. Default 0.
$args['metrics']string[]Metrics to include as columns. Default ['pageviews'].
$args['filters']arrayFilters to apply. Default [].
$args['group_by']stringField to group rows by.
$args['limit']intMaximum number of rows. Default 0 (unlimited).
$args['id']stringDatatable identifier (e.g. statistics_pages). Added in v3.4.2. Passed through to the burst_datatable_pre_data filter so integrations can short-circuit per-datatable, and used by the REST layer to enforce a per-datatable metric allow-list.

Return shape:

Show code
[
'columns' => [
[ 'name' => string, 'id' => string, 'sortable' => 'true', 'right' => 'true' ],
// …
],
'data' => array[], // raw rows from the database
'metrics' => string[],
]

Integrations can short-circuit the default query by returning a prebuilt data array via the burst_datatable_pre_data filter. See burst_datatable_pre_data.

Changed in v3.4.2: the generic data/datatable and data/ecommerce/datatable REST endpoints now return 403 Forbidden. Callers must use the granular per-datatable endpoints data/datatable/{id} and data/ecommerce/datatable/{id} (e.g. data/datatable/statistics_pages). The endpoint segment {id} is set as $args['id'] and used to look up the allowed metrics from App::get_datatable_metric_allow_list(); requested metrics are intersected against that allow-list before the query runs. Unknown datatable IDs return 404. Update any custom JavaScript or server-side code that called the generic endpoints. Changed in v3.4.1: when a metric key has no registered label, the column name now falls back to a humanized form of the key (e.g. my_custom_metricMy Custom Metric) via ucwords( str_replace( '_', ' ', $metric ) ) instead of triggering an undefined-index notice. Custom metrics added via burst_select_sql_for_metric therefore render with a sensible default header even when burst_allowed_metrics_labels is not used.

Added in v3.4.1: this method is also exposed to AI agents through the WordPress Abilities API as burst/data with type: 'datatable' when the enable_abilities_api option is on. The ability reformats the response into dimensions, metrics, rows, and row_count keys, and normalizes a few group_by aliases (e.g. utm_sourcesource).

Changed in v3.4.2: the burst/data ability now requires a datatable_id input when type is datatable. Accepted values are statistics_pages, statistics_parameters, statistics_referrers, sources_countries, sources_campaigns, sales_products, subscription_products, and sources_referrers. Requested metrics are intersected against the allow-list for that datatable; an unknown datatable_id returns burst_abilities_unknown_datatable. The pro burst/sales and burst/subscriptions-data abilities also accept the same allow-list-aware metrics (with a fallback when none of the requested metrics are valid) plus optional filters and a limit capped to 500.


get_dummy_datatable_data()

Added in v3.4.2. Returns a fixed-size array of synthetic datatable rows for preview, onboarding, and demo contexts. Each row is a fully populated record with realistic-looking values for every metric in the statistics_pages allow-list, including ecommerce metrics shaped as { currency, value }.

This method is wired up via the burst_datatable_pre_data filter on the App class: when $args['id'] is 'dummy_data', the filter returns the dummy rows and short-circuits the database query.

Show code
$rows = $statistics->get_dummy_datatable_data();
// [
// [
// 'page_url' => '/about-us',
// 'pageviews' => 2317,
// 'visitors' => 1689,
// 'sessions' => 1812,
// 'bounce_rate' => 42.3,
// 'avg_time_on_page' => 274,
// 'entrances' => 968,
// 'exit_rate' => 31.7,
// 'conversions' => 142,
// 'conversion_rate' => 6.1,
// 'sales' => 47,
// 'revenue' => [ 'currency' => 'USD', 'value' => 4823 ],
// 'sales_conversion_rate' => 2.0,
// 'page_value' => [ 'currency' => 'USD', 'value' => 2.08 ],
// ],
// // …15 rows total
// ]

Values are generated with wp_rand() and are not stable between calls; do not use them as fixtures in tests that assert exact numbers.


App::get_datatable_metric_allow_list()

Added in v3.4.2. Returns the per-datatable metric allow-list used by both the REST routes and the abilities API to gate which metrics each granular datatable endpoint can expose. The list is keyed by datatable id.

Show code
$allow_list = \Burst\burst_loader()->admin->app->get_datatable_metric_allow_list();
// [
// 'statistics_pages' => [ 'page_url', 'pageviews', 'visitors', ... ],
// 'statistics_parameters' => [ 'parameter', 'parameters', 'visitors', ... ],
// 'statistics_referrers' => [ 'referrer', 'visitors', 'sessions', ... ],
// 'dummy_data' => [ 'page_url', 'pageviews', 'visitors', ... ],
// // Pro adds: sources_countries, sources_campaigns, sales_products, subscription_products, sources_referrers
// ]

The result is filterable via burst_datatable_metric_allow_list. Add an entry for any custom datatable id used by your integration; without an entry the REST handler responds with 404 Unknown datatable endpoint. and the abilities API returns burst_abilities_unknown_datatable.


get_var( Query_Data $qd )

Executes a query and returns a single scalar value.

Show code
$qd = new Query_Data(
'monthly_pageviews',
[ 'date_start' => $start, 'date_end' => $end, 'select' => [ 'pageviews' ] ]
);
$val = $statistics->get_var( $qd );

Changed in v3.4.2.1: the executed SQL is automatically prefixed with a MAX_EXECUTION_TIME optimizer hint resolved via get_query_timeout_ms(). When MySQL aborts the query because the timeout was exceeded, get_var() logs the timeout and returns null rather than surfacing the error. Defaults are 30 s for foreground requests and 900 s for cron, both filterable via burst_query_timeout_ms and burst_query_timeout_ms_background.


get_row( Query_Data $qd, string $output_type = 'OBJECT' )

Executes a query and returns a single row.

$row = $statistics->get_row( $qd, ARRAY_A );

$output_type accepts OBJECT, ARRAY_A, or ARRAY_N.

Changed in v3.4.2.1: results are cached in the WordPress object cache for the duration returned by burst_query_results_cache_ttl (default 30 s; 300 s for date ranges longer than 30 days). When a persistent object cache is active, concurrent requests for the same cache key are deduplicated via a single-flight lock — only the first request runs the heavy query, followers either pick up the cached result or return null immediately to prevent thundering-herd fan-out. If MySQL aborts the query because the MAX_EXECUTION_TIME hint was exceeded, get_row() logs the timeout, writes a short-lived cooldown marker so the same key is not retried immediately, and returns null.


get_results( Query_Data $qd, string $output_type = 'OBJECT' )

Executes a query and returns all matching rows.

$rows = $statistics->get_results( $qd, ARRAY_A );

Changed in v3.4.2.1: results are cached, single-flighted, and timeout-protected with the same semantics as get_row(). When the query times out, get_results() logs the timeout, writes a cooldown marker for the cache key, and returns [] rather than surfacing the error. Followers that lose the single-flight race also return [] if the leader has not produced a cached result within burst_query_single_flight_wait_ms (default 1200 ms).


get_query_timeout_ms( Query_Data $qd )

Added in v3.4.2.1. Resolves the per-query MAX_EXECUTION_TIME (in milliseconds) applied to get_var(), get_row(), and get_results(). Background cron requests use a longer ceiling than foreground dashboard requests so that aggregation and backfill jobs are not killed mid-run.

ContextDefaultFilter
Foreground (REST, admin)30 000 ms (30 s)burst_query_timeout_ms
Background (wp_doing_cron())900 000 ms (15 min)burst_query_timeout_ms_background

The foreground default can also be overridden via the burst_query_timeout_ms option (positive integer, in milliseconds). The filter always runs last and wins.

$timeout = $statistics->get_query_timeout_ms( $qd );
// e.g. 30000

build_raw_sql( Query_Data $data )

Builds and returns the raw SQL string from a Query_Data object without executing it. Useful for debugging.

$sql = $statistics->build_raw_sql( $qd );
error_log( $sql );
caution

The returned SQL is built for direct use with $wpdb. Do not attempt to re-prepare it. The string returned here does not include the MAX_EXECUTION_TIME optimizer hint that get_var()/get_row()/get_results() prepend at execution time.


get_sql_select_for_metric( string $metric, Query_Data $query_data )

Returns the SQL fragment for a single metric. Respects the exclude_bounces flag.

$sql = $statistics->get_sql_select_for_metric( 'visitors', $qd );
// 'COUNT(DISTINCT statistics.uid)'

Changed in v3.3.0: browser_id, platform_id, device_id, and first_time_visit now resolve to sessions.* instead of statistics.*. For example:

Show code
$sql = $statistics->get_sql_select_for_metric( 'browser_id', $qd );
// 'sessions.browser_id'

$sql = $statistics->get_sql_select_for_metric( 'first_time_visit', $qd );
// 'sessions.first_time_visit'

Changed in v3.4.2.1: the bounces and bounce_rate metrics now read directly from sessions.bounce / sessions.ID instead of from the removed session_bounces derived table. The non-bounce predicate used when exclude_bounces is true is now COALESCE(sessions.bounce, 0) = 0. Custom code that filtered against session_bounces.bounce or session_bounces.session_id must be updated to use the sessions.* columns instead.


should_recommend_object_cache()

Added in v3.4.2.1. Static helper that returns true when Burst has recorded a slow analytics query and no persistent object cache is active. Used to drive the "persistent object cache recommended" admin task; safe to call from any context.

if ( \Burst\Admin\Statistics\Statistics::should_recommend_object_cache() ) {
// Surface a hint to enable Redis or Memcached.
}

Decision logic:

  • Returns false immediately when wp_using_ext_object_cache() is true.
  • Returns false when the burst_query_stats table does not yet exist.
  • Otherwise reads MAX(max_execution_time) from burst_query_stats and returns true when it meets or exceeds the threshold returned by the burst_object_cache_recommendation_threshold_seconds filter (default 10 seconds; clamped to a 0.1 s floor).

format_number( int $number, int $precision = 2 )

Formats a number with locale-aware separators and shorthand suffixes for large values (k, M, G, …).

echo $statistics->format_number( 123456 ); // '123k'
echo $statistics->format_number( 1234 ); // '1,234'

format_uplift( float $original_value, float $new_value )

Returns a formatted uplift string such as +12% or -5%, or an empty string when there is no change.


calculate_uplift( float $original_value, float $new_value )

Returns the percentage change as an integer.


calculate_uplift_status( float $original_value, float $new_value )

Returns 'positive', 'negative', or ''.


get_lookup_table_name_by_id( string $item, int $id )

Resolves a lookup-table ID to a human-readable name. Results are object-cached.

$name = $statistics->get_lookup_table_name_by_id( 'device', 2 );
// 'mobile'

$item must be one of: browser, browser_version, platform, device.


is_campaign_conversion_query( Query_Data $data )

Returns true when the query selects campaign parameters together with conversion or sales metrics.


is_parameter_conversion_query( Query_Data $data )

Returns true when the query selects URL parameters together with conversion or sales metrics.


Goal statistics

The Goal_Statistics class handles goal-specific queries.

$goal_stats = \Burst\burst_loader()->admin->goal_statistics;

get_goals_data( array $args )

Returns the full data payload for a goal detail card.

Parameters:

ParameterTypeDescription
$args['goal_id']intGoal ID to query. Default 0.
$args['date_start']intStart timestamp. Default 0.
$args['date_end']intEnd timestamp. Default 0.

Return shape (abbreviated):

Show code
[
'today' => [ 'value' => int, 'tooltip' => string ],
'total' => [ 'value' => int, 'tooltip' => string ],
'topPerformer' => [ 'title' => string, 'value' => int, 'tooltip' => string ],
'conversionMetric' => [ 'title' => string, 'value' => int, 'tooltip' => string, 'icon' => string ],
'conversionPercentage'=> [ 'title' => string, 'value' => int, 'tooltip' => string ],
'bestDevice' => [ 'title' => string, 'value' => int, 'tooltip' => string, 'icon' => mixed ],
'dateCreated' => int,
'dateStart' => int,
'dateEnd' => int,
'status' => string,
'goalId' => int,
]

get_live_goals_count( array $args )

Returns the number of goal completions for a given goal since midnight today.

$count = $goal_stats->get_live_goals_count( [ 'goal_id' => 3 ] );

Hooks reference

burst_on_page_offset

Adjusts the number of seconds added to a visitor's last-seen time before they are considered to have left a page. Used in live traffic and live visitor calculations.

Parameters:

ParameterTypeDescription
$offsetintOffset in seconds. Default 60.

Example:

add_filter( 'burst_on_page_offset', function( $offset ) {
return 90; // consider visitors active for 90 s after their last hit
} );

burst_live_traffic_args

Filters the Query_Data object used to fetch the raw live traffic rows before it is executed.

Parameters:

ParameterTypeDescription
$qdQuery_DataQuery object for the live traffic query.

Example:

Show code
add_filter( 'burst_live_traffic_args', function( $qd ) {
$qd->limit = 50; // cap live traffic at 50 rows
return $qd;
} );

burst_possible_filters_with_prefix

Defines the mapping from filter keys to their fully-qualified SQL column references. Extend this to add custom filterable columns.

Parameters:

ParameterTypeDescription
$filtersarray<string, string>Map of filter key → table.column.

Changed in v3.3.0: The built-in mappings for browser, browser_id, platform, platform_id, device, device_id, and new_visitor now resolve to sessions.* instead of statistics.*.

Changed in v3.4.2.1: The built-in bounces mapping now resolves to sessions.bounce instead of session_bounces.bounce. Custom code that overrode the mapping to reach into the old session_bounces derived table must be updated.

Example:

Show code
add_filter( 'burst_possible_filters_with_prefix', function( $filters ) {
$filters['my_custom_column'] = 'statistics.my_custom_column';
return $filters;
} );

burst_mappable_filters

Defines which filter keys should be resolved through the lookup tables (i.e. their string value is converted to an integer ID before querying).

Parameters:

ParameterTypeDescription
$mappablestring[]List of filter keys that map to lookup table IDs. Default: ['browser', 'browser_version', 'platform', 'device'].

Example:

Show code
add_filter( 'burst_mappable_filters', function( $mappable ) {
$mappable[] = 'my_lookup_filter';
return $mappable;
} );

burst_datatable_data

Filters the raw data rows returned for a datatable before they are sent to the client.

Parameters:

ParameterTypeDescription
$dataarrayArray of result rows (associative).
$qdQuery_DataThe query object that produced the data.

Example:

Show code
add_filter( 'burst_datatable_data', function( $data, $qd ) {
foreach ( $data as &$row ) {
$row['pageviews'] = max( 0, (int) $row['pageviews'] );
}
return $data;
}, 10, 2 );

burst_datatable_pre_data

Short-circuits get_datatables_data() so integrations can supply prebuilt rows without running the default SQL query. Return null to let the default query run, or an array of rows to skip it. When an array is returned, the outer method still applies the configured metric columns and labels.

Parameters:

ParameterTypeDescription
$dataarray|nullPrebuilt rows, or null to fall through to the default query.
$argsarrayThe original $args array passed to get_datatables_data(), including the v3.4.2 id key when the call came from a granular datatable endpoint.

Example:

Show code
add_filter( 'burst_datatable_pre_data', function( $data, $args ) {
if ( ( $args['id'] ?? '' ) !== 'my_custom_datatable' ) {
return $data;
}
return my_plugin_build_rows( $args );
}, 10, 2 );

burst_datatable_metric_allow_list

Added in v3.4.2. Filters the per-datatable metric allow-list returned by App::get_datatable_metric_allow_list(). Use this to register the metrics that a custom datatable id is allowed to expose. Without an entry for an id, REST requests to data/datatable/{id} (or data/ecommerce/datatable/{id}) return 404, and the burst/data ability rejects the id as unknown.

Parameters:

ParameterTypeDescription
$allow_listarray<string, string[]>Map of datatable id → list of allowed metric keys.

Example:

Show code
add_filter( 'burst_datatable_metric_allow_list', function( $allow_list ) {
$allow_list['my_plugin_orders'] = [
'product',
'sales',
'revenue',
'avg_order_value',
];
return $allow_list;
} );

Pair this with a burst_datatable_pre_data callback (matched on $args['id']) if the rows do not come from the default Burst tables.


burst_select_sql_for_metric

Provides SQL for custom metric keys that are not built into the Statistics class. Return false to indicate no SQL is available for the metric.

Parameters:

ParameterTypeDescription
$metricstringThe metric key that was not matched internally.

Example:

Show code
add_filter( 'burst_select_sql_for_metric', function( $metric ) {
if ( $metric === 'my_metric' ) {
return 'COUNT(DISTINCT statistics.my_column)';
}
return $metric;
} );

burst_build_where_clause

Filters the assembled WHERE clause string before it is included in the final SQL.

Parameters:

ParameterTypeDescription
$wherestringCurrent WHERE fragment (may be empty or start with AND).
$dataQuery_DataThe current query object.

Example:

Show code
add_filter( 'burst_build_where_clause', function( $where, $data ) {
$where .= " AND statistics.page_type != 'attachment' ";
return $where;
}, 10, 2 );

burst_available_joins

Defines the available JOIN configurations that the query builder can auto-detect and include.

Parameters:

ParameterTypeDescription
$joinsarrayMap of alias → join configuration.
$dataQuery_DataThe current query object.

Each join configuration has the shape:

Show code
[
'table' => 'burst_sessions', // table name without prefix, or raw subquery SQL
'on' => 'statistics.session_id = sessions.ID',
'type' => 'INNER', // INNER or LEFT
'depends_on' => [], // aliases that must be joined first
]

Changed in v3.3.0: The built-in session_bounces join now reads directly from burst_sessions (SELECT ID as session_id, bounce FROM burst_sessions) instead of aggregating bounce values from burst_statistics. Custom code that relied on the previous subquery shape should be updated accordingly.

:::caution Breaking change Removed in v3.4.2.1: the built-in session_bounces join has been removed entirely. Bounce metrics are now resolved directly against sessions.bounce and sessions.ID via the always-present sessions join. Custom filters or burst_select_sql_for_metric callbacks that referenced session_bounces.bounce or session_bounces.session_id must be rewritten to use sessions.bounce and sessions.ID. If your integration genuinely needs the old derived-table shape, re-register it explicitly through this filter. :::

Example:

Show code
add_filter( 'burst_available_joins', function( $joins, $data ) {
$joins['my_table'] = [
'table' => 'my_custom_table',
'on' => 'statistics.ID = my_table.statistic_id',
'type' => 'LEFT',
'depends_on' => [],
];
return $joins;
}, 10, 2 );

burst_allowed_metric_keys

Filters the list of metric keys permitted in strict (non-admin) mode.

Parameters:

ParameterTypeDescription
$keysstring[]Allowed metric keys.
$is_strictboolWhether strict mode is active.

Example:

Show code
add_filter( 'burst_allowed_metric_keys', function( $keys, $is_strict ) {
if ( $is_strict ) {
$keys[] = 'bounce_rate';
}
return $keys;
}, 10, 2 );

burst_allowed_metrics

Filters the full associative array of allowed metrics (key → label) after strict-mode filtering is applied.

Parameters:

ParameterTypeDescription
$metricsarray<string, string>Allowed metrics map.
$is_strictboolWhether strict mode is active.

Example:

Show code
add_filter( 'burst_allowed_metrics', function( $metrics, $is_strict ) {
$metrics['my_metric'] = 'My Metric';
return $metrics;
}, 10, 2 );

burst_statistics_allowed_filter_keys

Filters the list of filter keys accepted by Query_Data.

Parameters:

ParameterTypeDescription
$keysstring[]Allowed filter keys.
$is_strictboolWhether strict mode is active.

Example:

Show code
add_filter( 'burst_statistics_allowed_filter_keys', function( $keys, $is_strict ) {
$keys[] = 'my_custom_filter';
return $keys;
}, 10, 2 );

burst_statistics_allowed_group_by

Filters the list of accepted group_by values.

Parameters:

ParameterTypeDescription
$group_bystring[]Allowed group-by fields.
$is_strictboolWhether strict mode is active.

Example:

Show code
add_filter( 'burst_statistics_allowed_group_by', function( $group_by, $is_strict ) {
$group_by[] = 'my_custom_column';
return $group_by;
}, 10, 2 );

burst_statistics_allowed_order_by

Filters the list of accepted order_by values.

Parameters:

ParameterTypeDescription
$order_bystring[]Allowed order-by clauses (e.g. 'pageviews DESC').

Example:

Show code
add_filter( 'burst_statistics_allowed_order_by', function( $order_by, $is_strict ) {
$order_by[] = 'my_column DESC';
$order_by[] = 'my_column ASC';
return $order_by;
}, 10, 2 );

burst_allowed_metrics_labels

Filters the full map of metric keys to their translated display labels.

Parameters:

ParameterTypeDescription
$labelsarray<string, string>Map of metric key → translated label.

Example:

Show code
add_filter( 'burst_allowed_metrics_labels', function( $labels ) {
$labels['my_metric'] = __( 'My Metric', 'my-plugin' );
return $labels;
} );

burst_allowed_post_types

Filters the list of post types accepted as a valid value for the page_type filter in strict mode.

Parameters:

ParameterTypeDescription
$post_typesarrayArray of public post type slugs. Default: get_post_types( ['public' => true] ).

Example:

Show code
add_filter( 'burst_allowed_post_types', function( $post_types ) {
$post_types[] = 'my_private_type';
return $post_types;
} );

burst_query_timeout_ms

Added in v3.4.2.1. Filters the foreground (REST/admin) query timeout in milliseconds applied as a MAX_EXECUTION_TIME optimizer hint on the queries executed by get_var(), get_row(), and get_results().

Parameters:

ParameterTypeDescription
$timeout_msintResolved foreground timeout (default 30 000 ms, or the value of the burst_query_timeout_ms option when it is positive).
$qdQuery_DataThe query object that is about to run.

Example:

Show code
add_filter( 'burst_query_timeout_ms', function( $timeout_ms, $qd ) {
if ( $qd->get_id() === 'top_pages_by_device' ) {
return 5000; // 5 s for this specific query family
}
return $timeout_ms;
}, 10, 2 );

burst_query_timeout_ms_background

Added in v3.4.2.1. Filters the background (cron) query timeout in milliseconds. Used instead of burst_query_timeout_ms whenever wp_doing_cron() is true, so aggregation and backfill jobs get more headroom than dashboard requests.

Parameters:

ParameterTypeDescription
$timeout_msintResolved background timeout. Default 900 000 ms (15 min).
$qdQuery_DataThe query object that is about to run.

Example:

add_filter( 'burst_query_timeout_ms_background', function( $timeout_ms ) {
return 1800000; // 30 min for background queries on this site
} );

burst_query_results_cache_ttl

Added in v3.4.2.1. Filters the object-cache TTL, in seconds, used by get_row() and get_results() to store query results. Set to 0 to disable result caching for the query.

Parameters:

ParameterTypeDescription
$ttlintResolved TTL in seconds. Default 30 s; 300 s when the query's date range is longer than 30 days. The burst_query_results_cache_ttl option overrides the default when set to a non-negative integer.
$qdQuery_DataThe query object whose result is about to be cached.

Example:

Show code
add_filter( 'burst_query_results_cache_ttl', function( $ttl, $qd ) {
if ( $qd->get_id() === 'live_dashboard_widget' ) {
return 0; // never cache this query
}
return $ttl;
}, 10, 2 );

burst_query_single_flight_enabled

Added in v3.4.2.1. Filters whether single-flight deduplication is active for cached query results. By default it is enabled only when a shared external object cache is in use (wp_using_ext_object_cache() is true), because the local object cache is request-scoped and provides no cross-request coordination.

Parameters:

ParameterTypeDescription
$enabledboolWhether single-flight is enabled for this query.
$qdQuery_DataThe query object that is about to run.

Example:

Show code
add_filter( 'burst_query_single_flight_enabled', function( $enabled, $qd ) {
if ( $qd->get_id() === 'expensive_compare_block' ) {
return true; // force single-flight even without an external cache
}
return $enabled;
}, 10, 2 );

burst_query_single_flight_wait_ms

Added in v3.4.2.1. Filters how long, in milliseconds, follower requests wait for the leader query to populate the cache before returning an empty result. Lower values reduce tail latency under contention; higher values increase cache-hit yield.

Parameters:

ParameterTypeDescription
$wait_msintResolved wait in milliseconds. Default 1200.
$qdQuery_DataThe query object that is being followed.

burst_query_single_flight_lock_ttl

Added in v3.4.2.1. Filters the TTL (in seconds) of the cache lock used to elect a single-flight leader for a given cache key. Defaults to ceil( $timeout_ms / 1000 ) + 2 with a floor of 5 seconds, so the lock always outlives the underlying MAX_EXECUTION_TIME.

Parameters:

ParameterTypeDescription
$lock_ttl_secintResolved lock TTL in seconds.
$qdQuery_DataThe query object that is about to run.
$timeout_msintEffective query timeout in milliseconds.

burst_query_timeout_cooldown_ttl

Added in v3.4.2.1. Filters the cooldown TTL (in seconds) written to the object cache after a query times out. While the cooldown marker is present, repeat calls for the same cache key short-circuit and return null/[] without re-running the query, preventing the same expensive query from repeatedly hitting the database after a timeout.

Parameters:

ParameterTypeDescription
$cooldown_ttlintResolved cooldown TTL in seconds. Defaults to max( 30, ceil( $timeout_ms / 1000 ) ).
$qdQuery_DataThe query object whose timeout is being recorded.
$timeout_msintEffective query timeout in milliseconds.

Example:

add_filter( 'burst_query_timeout_cooldown_ttl', function( $cooldown_ttl ) {
return 120; // 2-minute cooldown after any timeout
} );

burst_object_cache_recommendation_threshold_seconds

Added in v3.4.2.1. Filters the slowest-query threshold (in seconds) that triggers the "persistent object cache recommended" admin task surfaced by should_recommend_object_cache(). The effective threshold is clamped to a 0.1 s floor.

Parameters:

ParameterTypeDescription
$threshold_secondsfloatThreshold in seconds. Default 10.0.

Example:

add_filter( 'burst_object_cache_recommendation_threshold_seconds', function() {
return 5.0; // recommend an object cache when any query exceeds 5 s
} );

Custom queries: end-to-end example

The following example queries pageviews and unique visitors grouped by page URL for the current month, filtered to desktop visitors only, and returns the top 10 results.

Show code
use Burst\Admin\Statistics\Query_Data;

$statistics = \Burst\burst_loader()->admin->statistics;

$start = strtotime( 'first day of this month midnight' );
$end = time();

$qd = new Query_Data(
'top_desktop_pages_this_month',
[
'date_start' => $start,
'date_end' => $end,
'select' => [ 'page_url', 'pageviews', 'visitors' ],
'filters' => [ 'device' => 'desktop' ],
'group_by' => 'page_url',
'order_by' => 'pageviews DESC',
'limit' => 10,
]
);

$rows = $statistics->get_results( $qd, ARRAY_A );

foreach ( $rows as $row ) {
printf(
"%s — %s pageviews, %s visitors\n",
$row['page_url'],
$statistics->format_number( (int) $row['pageviews'] ),
$statistics->format_number( (int) $row['visitors'] )
);
}

Adding a custom WHERE clause (admin context only)

Show code
$qd = new Query_Data(
'blog_pageviews_this_month',
[
'date_start' => $start,
'date_end' => $end,
'select' => [ 'pageviews', 'page_url' ],
'group_by' => 'page_url',
'custom_where' => 'AND statistics.page_url LIKE %s',
'custom_where_parameters' => [ '%/blog/%' ],
]
);

$rows = $statistics->get_results( $qd, ARRAY_A );
caution

custom_where and custom_select are only processed for users with admin access (non-strict mode). In strict mode these parameters are silently discarded. All custom SQL is validated against a blocklist of dangerous keywords and patterns before use.